diff options
-rw-r--r-- | CMakeLists.txt | 6 | ||||
-rw-r--r-- | redfish-core/include/redfish.hpp | 16 | ||||
-rw-r--r-- | redfish-core/lib/log_services.hpp | 460 | ||||
-rw-r--r-- | redfish-core/lib/systems.hpp | 3 |
4 files changed, 432 insertions, 53 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 50a6bd40a2..b6ece0e584 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,11 @@ option (BMCWEB_ENABLE_STATIC_HOSTING "Enable hosting of static files. For example, redfish schema and webui files" ON) option ( + BMCWEB_ENABLE_REDFISH_BMC_JOURNAL + "Enable BMC journal access through redfish" OFF +) + +option ( BMCWEB_ENABLE_REDFISH_RAW_PECI "Enable PECI transactions through redfish" OFF ) @@ -252,4 +257,5 @@ target_compile_definitions ( $<$<BOOL:${BMCWEB_INSECURE_DISABLE_XSS_PREVENTION}>: -DBMCWEB_INSECURE_DISABLE_XSS_PREVENTION> $<$<BOOL:${BMCWEB_ENABLE_REDFISH_RAW_PECI}>: -DBMCWEB_ENABLE_REDFISH_RAW_PECI> $<$<BOOL:${BMCWEB_ENABLE_REDFISH_CPU_LOG}>: -DBMCWEB_ENABLE_REDFISH_CPU_LOG> + $<$<BOOL:${BMCWEB_ENABLE_REDFISH_BMC_JOURNAL}>: -DBMCWEB_ENABLE_REDFISH_BMC_JOURNAL> )
\ No newline at end of file diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp index 2bf48bf6c2..c2d64c2010 100644 --- a/redfish-core/include/redfish.hpp +++ b/redfish-core/include/redfish.hpp @@ -72,10 +72,18 @@ class RedfishService nodes.emplace_back( std::make_unique<VlanNetworkInterfaceCollection>(app)); nodes.emplace_back(std::make_unique<VlanNetworkInterface>(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)); + + nodes.emplace_back(std::make_unique<SystemLogServiceCollection>(app)); + nodes.emplace_back(std::make_unique<EventLogService>(app)); + nodes.emplace_back(std::make_unique<EventLogEntryCollection>(app)); + nodes.emplace_back(std::make_unique<EventLogEntry>(app)); + + nodes.emplace_back(std::make_unique<BMCLogServiceCollection>(app)); +#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL + nodes.emplace_back(std::make_unique<BMCJournalLogService>(app)); + nodes.emplace_back(std::make_unique<BMCJournalLogEntryCollection>(app)); + nodes.emplace_back(std::make_unique<BMCJournalLogEntry>(app)); +#endif #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG nodes.emplace_back(std::make_unique<CPULogService>(app)); diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp index 66a852e906..a0c73517b6 100644 --- a/redfish-core/lib/log_services.hpp +++ b/redfish-core/lib/log_services.hpp @@ -254,11 +254,367 @@ static bool getTimestampFromID(crow::Response &res, const std::string &entryID, return true; } -class LogServiceCollection : public Node +class SystemLogServiceCollection : public Node { public: template <typename CrowApp> - LogServiceCollection(CrowApp &app) : + SystemLogServiceCollection(CrowApp &app) : + Node(app, "/redfish/v1/Systems/<str>/LogServices/", 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: + /** + * Functions triggers appropriate requests on DBus + */ + 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); + const std::string &name = params[0]; + // Collections don't include the static data added by SubRoute because + // it has a duplicate entry for members + asyncResp->res.jsonValue["@odata.type"] = + "#LogServiceCollection.LogServiceCollection"; + asyncResp->res.jsonValue["@odata.context"] = + "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection"; + asyncResp->res.jsonValue["@odata.id"] = + "/redfish/v1/Systems/" + name + "/LogServices"; + asyncResp->res.jsonValue["Name"] = "System Log Services Collection"; + asyncResp->res.jsonValue["Description"] = + "Collection of LogServices for this Computer System"; + nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; + logServiceArray = nlohmann::json::array(); + logServiceArray.push_back({{"@odata.id", "/redfish/v1/Systems/" + name + + "/LogServices/EventLog"}}); + asyncResp->res.jsonValue["Members@odata.count"] = + logServiceArray.size(); + } +}; + +class EventLogService : public Node +{ + public: + template <typename CrowApp> + EventLogService(CrowApp &app) : + Node(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/", + 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); + + const std::string &name = params[0]; + asyncResp->res.jsonValue["@odata.id"] = + "/redfish/v1/Systems/" + name + "/LogServices/EventLog"; + asyncResp->res.jsonValue["@odata.type"] = + "#LogService.v1_1_0.LogService"; + asyncResp->res.jsonValue["@odata.context"] = + "/redfish/v1/$metadata#LogService.LogService"; + asyncResp->res.jsonValue["Name"] = "Event Log Service"; + asyncResp->res.jsonValue["Description"] = "System Event Log Service"; + asyncResp->res.jsonValue["Id"] = "Event Log"; + asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; + asyncResp->res.jsonValue["Entries"] = { + {"@odata.id", + "/redfish/v1/Systems/" + name + "/LogServices/EventLog/Entries"}}; + } +}; + +static int fillEventLogEntryJson(const std::string &systemName, + const std::string &bmcLogEntryID, + const boost::string_view &messageID, + sd_journal *journal, + nlohmann::json &bmcLogEntryJson) +{ + // Get the Log Entry contents + int ret = 0; + + boost::string_view msg; + ret = getJournalMetadata(journal, "MESSAGE", msg); + if (ret < 0) + { + BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret); + return 1; + } + + // Get the severity from the PRIORITY field + int severity = 8; // Default to an invalid priority + ret = getJournalMetadata(journal, "PRIORITY", 10, severity); + if (ret < 0) + { + BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret); + return 1; + } + + // Get the MessageArgs from the journal entry by finding all of the + // REDFISH_MESSAGE_ARG_x fields + const void *data; + size_t length; + std::vector<std::string> messageArgs; + SD_JOURNAL_FOREACH_DATA(journal, data, length) + { + boost::string_view field(static_cast<const char *>(data), length); + if (field.starts_with("REDFISH_MESSAGE_ARG_")) + { + // Get the Arg number from the field name + field.remove_prefix(sizeof("REDFISH_MESSAGE_ARG_") - 1); + if (field.empty()) + { + continue; + } + int argNum = std::strtoul(field.data(), nullptr, 10); + if (argNum == 0) + { + continue; + } + // Get the Arg value after the "=" character. + field.remove_prefix(std::min(field.find("=") + 1, field.size())); + // Make sure we have enough space in messageArgs + if (argNum > messageArgs.size()) + { + messageArgs.resize(argNum); + } + messageArgs[argNum - 1] = field.to_string(); + } + } + + // Get the Created time from the timestamp + std::string entryTimeStr; + if (!getEntryTimestamp(journal, entryTimeStr)) + { + return 1; + } + + // 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/Systems/" + systemName + + "/LogServices/EventLog/Entries/" + bmcLogEntryID}, + {"Name", "System Event Log Entry"}, + {"Id", bmcLogEntryID}, + {"Message", msg}, + {"MessageId", messageID}, + {"MessageArgs", std::move(messageArgs)}, + {"EntryType", "Event"}, + {"Severity", + severity <= 2 ? "Critical" + : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""}, + {"Created", std::move(entryTimeStr)}}; + return 0; +} + +class EventLogEntryCollection : public Node +{ + public: + template <typename CrowApp> + EventLogEntryCollection(CrowApp &app) : + Node(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/", + 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); + long skip = 0; + long top = maxEntriesPerPage; // Show max entries by default + if (!getSkipParam(asyncResp->res, req, skip)) + { + return; + } + if (!getTopParam(asyncResp->res, req, top)) + { + return; + } + const std::string &name = params[0]; + // 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/Systems/" + name + "/LogServices/EventLog/Entries"; + asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; + asyncResp->res.jsonValue["Description"] = + "Collection of System Event Log Entries"; + nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; + logEntryArray = nlohmann::json::array(); + + // Go through the journal and 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); + messages::internalError(asyncResp->res); + return; + } + std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( + journalTmp, sd_journal_close); + journalTmp = nullptr; + uint64_t entryCount = 0; + SD_JOURNAL_FOREACH(journal.get()) + { + // Look for only journal entries that contain a REDFISH_MESSAGE_ID + // field + boost::string_view messageID; + ret = getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", + messageID); + if (ret < 0) + { + continue; + } + + entryCount++; + // Handle paging using skip (number of entries to skip from the + // start) and top (number of entries to display) + if (entryCount <= skip || entryCount > skip + top) + { + continue; + } + + std::string idStr; + if (!getUniqueEntryID(journal.get(), idStr)) + { + continue; + } + + logEntryArray.push_back({}); + nlohmann::json &bmcLogEntry = logEntryArray.back(); + if (fillEventLogEntryJson(name, idStr, messageID, journal.get(), + bmcLogEntry) != 0) + { + messages::internalError(asyncResp->res); + return; + } + } + asyncResp->res.jsonValue["Members@odata.count"] = entryCount; + if (skip + top < entryCount) + { + asyncResp->res.jsonValue["Members@odata.nextLink"] = + "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" + + std::to_string(skip + top); + } + } +}; + +class EventLogEntry : public Node +{ + public: + EventLogEntry(CrowApp &app) : + Node(app, + "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/", + std::string(), 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() != 2) + { + messages::internalError(asyncResp->res); + return; + } + const std::string &name = params[0]; + const std::string &entryID = params[1]; + // Convert the unique ID back to a timestamp to find the entry + uint64_t ts = 0; + uint16_t index = 0; + if (!getTimestampFromID(asyncResp->res, entryID, ts, index)) + { + return; + } + + 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); + messages::internalError(asyncResp->res); + 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()); + } + // Confirm that the entry ID matches what was requested + std::string idStr; + if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID) + { + messages::resourceMissingAtURI(asyncResp->res, entryID); + return; + } + + // only use journal entries that contain a REDFISH_MESSAGE_ID + // field + boost::string_view messageID; + ret = + getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", messageID); + if (ret < 0) + { + messages::resourceNotFound(asyncResp->res, "LogEntry", name); + return; + } + + if (fillEventLogEntryJson(name, entryID, messageID, journal.get(), + asyncResp->res.jsonValue) != 0) + { + messages::internalError(asyncResp->res); + return; + } + } +}; + +class BMCLogServiceCollection : public Node +{ + public: + template <typename CrowApp> + BMCLogServiceCollection(CrowApp &app) : Node(app, "/redfish/v1/Managers/bmc/LogServices/") { // Collections use static ID for SubRoute to add to its parent, but only @@ -286,35 +642,37 @@ class LogServiceCollection : public Node asyncResp->res.jsonValue["@odata.type"] = "#LogServiceCollection.LogServiceCollection"; asyncResp->res.jsonValue["@odata.context"] = - "/redfish/v1/" - "$metadata#LogServiceCollection.LogServiceCollection"; + "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection"; 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 = asyncResp->res.jsonValue["Members"]; - logserviceArray = nlohmann::json::array(); - logserviceArray.push_back( - {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog"}}); + nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; + logServiceArray = nlohmann::json::array(); +#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL + logServiceArray.push_back( + {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}}); +#endif #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG - logserviceArray.push_back( + logServiceArray.push_back( {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/CpuLog"}}); #endif asyncResp->res.jsonValue["Members@odata.count"] = - logserviceArray.size(); + logServiceArray.size(); } }; -class BMCLogService : public Node +class BMCJournalLogService : public Node { public: template <typename CrowApp> - BMCLogService(CrowApp &app) : - Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/") + BMCJournalLogService(CrowApp &app) : + Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/") { // Set the id for SubRoute - Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/BmcLog"; + Node::json["@odata.id"] = + "/redfish/v1/Managers/bmc/LogServices/Journal"; entityPrivileges = { {boost::beast::http::verb::get, {{"Login"}}}, {boost::beast::http::verb::head, {{"Login"}}}, @@ -335,16 +693,16 @@ class BMCLogService : public Node "#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["Name"] = "Open BMC Journal Log Service"; + asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service"; + asyncResp->res.jsonValue["Id"] = "BMC Journal"; asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; } }; -static int fillBMCLogEntryJson(const std::string &bmcLogEntryID, - sd_journal *journal, - nlohmann::json &bmcLogEntryJson) +static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID, + sd_journal *journal, + nlohmann::json &bmcJournalLogEntryJson) { // Get the Log Entry contents int ret = 0; @@ -374,13 +732,13 @@ static int fillBMCLogEntryJson(const std::string &bmcLogEntryID, } // Fill in the log entry with the gathered data - bmcLogEntryJson = { + bmcJournalLogEntryJson = { {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, - {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/" + - bmcLogEntryID}, + {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" + + bmcJournalLogEntryID}, {"Name", "BMC Journal Entry"}, - {"Id", bmcLogEntryID}, + {"Id", bmcJournalLogEntryID}, {"Message", msg}, {"EntryType", "Oem"}, {"Severity", @@ -391,17 +749,17 @@ static int fillBMCLogEntryJson(const std::string &bmcLogEntryID, return 0; } -class BMCLogEntryCollection : public Node +class BMCJournalLogEntryCollection : public Node { public: template <typename CrowApp> - BMCLogEntryCollection(CrowApp &app) : - Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/") + BMCJournalLogEntryCollection(CrowApp &app) : + Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/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"; + "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; entityPrivileges = { {boost::beast::http::verb::get, {{"Login"}}}, {boost::beast::http::verb::head, {{"Login"}}}, @@ -432,10 +790,9 @@ class BMCLogEntryCollection : public Node asyncResp->res.jsonValue["@odata.type"] = "#LogEntryCollection.LogEntryCollection"; asyncResp->res.jsonValue["@odata.context"] = - "/redfish/v1/" - "$metadata#LogEntryCollection.LogEntryCollection"; + "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection"; asyncResp->res.jsonValue["@odata.id"] = - "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; + "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; asyncResp->res.jsonValue["Description"] = "Collection of BMC Journal Entries"; @@ -473,8 +830,9 @@ class BMCLogEntryCollection : public Node } logEntryArray.push_back({}); - nlohmann::json &bmcLogEntry = logEntryArray.back(); - if (fillBMCLogEntryJson(idStr, journal.get(), bmcLogEntry) != 0) + nlohmann::json &bmcJournalLogEntry = logEntryArray.back(); + if (fillBMCJournalLogEntryJson(idStr, journal.get(), + bmcJournalLogEntry) != 0) { messages::internalError(asyncResp->res); return; @@ -484,17 +842,17 @@ class BMCLogEntryCollection : public Node if (skip + top < entryCount) { asyncResp->res.jsonValue["Members@odata.nextLink"] = - "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" + + "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" + std::to_string(skip + top); } } }; -class BMCLogEntry : public Node +class BMCJournalLogEntry : public Node { public: - BMCLogEntry(CrowApp &app) : - Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/<str>/", + BMCJournalLogEntry(CrowApp &app) : + Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/", std::string()) { entityPrivileges = { @@ -542,8 +900,16 @@ class BMCLogEntry : public Node { sd_journal_next(journal.get()); } - if (fillBMCLogEntryJson(params[0], journal.get(), - asyncResp->res.jsonValue) != 0) + // Confirm that the entry ID matches what was requested + std::string idStr; + if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID) + { + messages::resourceMissingAtURI(asyncResp->res, entryID); + return; + } + + if (fillBMCJournalLogEntryJson(entryID, journal.get(), + asyncResp->res.jsonValue) != 0) { messages::internalError(asyncResp->res); return; @@ -582,8 +948,7 @@ class CPULogService : public Node asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_1_0.LogService"; asyncResp->res.jsonValue["@odata.context"] = - "/redfish/v1/" - "$metadata#LogService.LogService"; + "/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"; @@ -592,16 +957,14 @@ class CPULogService : public Node asyncResp->res.jsonValue["Actions"] = { {"Oem", {{"#CpuLog.Immediate", - {{"target", - "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" - "CpuLog.Immediate"}}}}}}; + {{"target", "/redfish/v1/Managers/bmc/LogServices/CpuLog/" + "Actions/Oem/CpuLog.Immediate"}}}}}}; #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI asyncResp->res.jsonValue["Actions"]["Oem"].push_back( {"#CpuLog.SendRawPeci", - {{"target", - "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" - "CpuLog.SendRawPeci"}}}); + {{"target", "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/" + "Oem/CpuLog.SendRawPeci"}}}); #endif } }; @@ -653,8 +1016,7 @@ class CPULogEntryCollection : public Node asyncResp->res.jsonValue["@odata.type"] = "#LogEntryCollection.LogEntryCollection"; asyncResp->res.jsonValue["@odata.context"] = - "/redfish/v1/" - "$metadata#LogEntryCollection.LogEntryCollection"; + "/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"; diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp index a1fbd0a5c3..67d0124e8a 100644 --- a/redfish-core/lib/systems.hpp +++ b/redfish-core/lib/systems.hpp @@ -712,6 +712,9 @@ class Systems : public Node {"ResetType@Redfish.AllowableValues", {"On", "ForceOff", "GracefulRestart", "GracefulShutdown"}}}; + res.jsonValue["LogServices"] = { + {"@odata.id", "/redfish/v1/Systems/" + name + "/LogServices"}}; + auto asyncResp = std::make_shared<AsyncResp>(res); getLedGroupIdentify( |