diff options
author | Andrew Geissler <geissonator@yahoo.com> | 2018-08-17 17:56:14 +0300 |
---|---|---|
committer | Anthony Wilson <wilsonan@us.ibm.com> | 2019-04-05 00:50:05 +0300 |
commit | cb92c03b1a8798657b9e03602e1ae851b2614e77 (patch) | |
tree | e0c4a9939a68cf15d1e508819e09daf7b2d1b9e8 | |
parent | 61adbda37aa63ae77585797587a7b4b6c3b61b25 (diff) | |
download | bmcweb-cb92c03b1a8798657b9e03602e1ae851b2614e77.tar.xz |
Initial redfish logging support
This was imported from a fork:
https://github.com/ampere-openbmc/bmcweb/commits/ampere-next/redfish-core/lib/logservices.hpp
Which had a series of commits from TungVuX and hyche
The initial patch is that code verbatim.
Follow up patches on top of this make the necessary changes
for current bmcweb:
- Move to sdbusplus
- C++ naming convention changes
- Clang format
- Some refactoring
Tested:
Verified new services work correctly when queried
e.g. curl -k -H "X-Auth-Token: $bmc_token" -X GET \
https://${bmc}/redfish/v1/Systems/system/LogServices/EventLog/Entries
displays the entries properly.
RedfishServiceValidator results:
/redfish/v1/Systems/system/LogServices
pass: 3
passGet: 1
skipOptional: 1
/redfish/v1/Systems/system/LogServices/EventLog
pass: 5
passGet: 1
skipOptional: 8
/redfish/v1/Systems/system/LogServices/EventLog/Entries
pass: 3
passGet: 1
skipOptional: 1
/redfish/v1/Systems/system/LogServices/EventLog/<str>
pass: 6
passGet: 1
skipOptional: 16
Sample Output: curl -k -H "X-Auth-Token: $bmc_token" -X GET https://${bmc}/redfish/v1/Systems/system/LogServices/EventLog/Entries
{
"@odata.context": "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection",
"@odata.id": "/redfish/v1/Systems/system/LogServices/EventLog/Entries",
"@odata.type": "#LogEntryCollection.LogEntryCollection",
"Description": "Collection of System Event Log Entries",
"Members": [
{
"@odata.context": "/redfish/v1/$metadata#LogEntry.LogEntry",
"@odata.id": "/redfish/v1/Systems/system/LogServices/EventLog/Entries/1",
"@odata.type": "#LogEntry.v1_4_0.LogEntry",
"Created": "2019-02-22T17:11:00+00:00",
"EntryType": "Event",
"Id": "1",
"Message": "example.xyz.openbmc_project.Example.Elog.AutoTestSimple",
"Name": "System DBus Event Log Entry",
"Severity": "Critical"
}
],
"Members@odata.count": 1,
"Name": "System Event Log Entries"
}
Change-Id: I422b0d0ec577ea734fecfb6f49101ec5ff45a556
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
Signed-off-by: Anthony Wilson <wilsonan@us.ibm.com>
-rw-r--r-- | CMakeLists.txt | 5 | ||||
-rw-r--r-- | crow/include/crow/utility.h | 30 | ||||
-rw-r--r-- | redfish-core/include/redfish.hpp | 3 | ||||
-rw-r--r-- | redfish-core/lib/log_services.hpp | 324 | ||||
-rw-r--r-- | redfish-core/lib/managers.hpp | 19 |
5 files changed, 352 insertions, 29 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 70e56fd00f..28fbe0ff60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,9 @@ option (BMCWEB_ENABLE_REDFISH_CPU_LOG "Enable CPU log service transactions option (BMCWEB_ENABLE_REDFISH_ONE_CHASSIS "Enable Redfish to assume only one chassis is present. All sensors will be assumed to be under this chassis. The chassis path is '/redfish/v1/Chassis/chassis'." OFF) +option (BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES "Enable DBUS log service + transactions through Redfish. Paths are under + '/redfish/v1/Systems/system/LogServices/EventLog/Entries'." OFF) # Insecure options. Every option that starts with a BMCWEB_INSECURE flag should # not be enabled by default for any platform, unless the author fully @@ -267,4 +270,6 @@ target_compile_definitions ( $<$<BOOL:${BMCWEB_ENABLE_REDFISH_CPU_LOG}>: -DBMCWEB_ENABLE_REDFISH_CPU_LOG> $<$<BOOL:${BMCWEB_ENABLE_REDFISH_BMC_JOURNAL}>: -DBMCWEB_ENABLE_REDFISH_BMC_JOURNAL> $<$<BOOL:${BMCWEB_ENABLE_REDFISH_ONE_CHASSIS}>: -DBMCWEB_ENABLE_REDFISH_ONE_CHASSIS> + $<$<BOOL:${BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES}>: -DBMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES> + ) diff --git a/crow/include/crow/utility.h b/crow/include/crow/utility.h index a07c0415af..63d2dece5f 100644 --- a/crow/include/crow/utility.h +++ b/crow/include/crow/utility.h @@ -2,7 +2,6 @@ #include "nlohmann/json.hpp" -#include <boost/utility/string_view.hpp> #include <cstdint> #include <cstring> #include <functional> @@ -712,5 +711,34 @@ inline void convertToLinks(std::string& s) s = std::regex_replace(s, nextLink, "$1<a href=\"$5\">$4</a>"); } +/** + * Method returns Date Time information according to requested format + * + * @param[in] time time in second since the Epoch + * + * @return Date Time according to requested format + */ +inline std::string getDateTime(const std::time_t& time) +{ + std::array<char, 128> dateTime; + std::string redfishDateTime("0000-00-00T00:00:00Z00:00"); + + if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z", + std::localtime(&time))) + { + // insert the colon required by the ISO 8601 standard + redfishDateTime = std::string(dateTime.data()); + redfishDateTime.insert(redfishDateTime.end() - 2, ':'); + } + + return redfishDateTime; +} + +inline std::string dateTimeNow() +{ + std::time_t time = std::time(nullptr); + return getDateTime(time); +} + } // namespace utility } // namespace crow diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp index 36b50e87c7..38832c23d6 100644 --- a/redfish-core/include/redfish.hpp +++ b/redfish-core/include/redfish.hpp @@ -103,6 +103,9 @@ class RedfishService nodes.emplace_back(std::make_unique<SystemsCollection>(app)); nodes.emplace_back(std::make_unique<Systems>(app)); nodes.emplace_back(std::make_unique<SystemActionsReset>(app)); +#ifdef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES + nodes.emplace_back(std::make_unique<DBusLogServiceActionsClear>(app)); +#endif for (const auto& node : nodes) { diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp index 61cbd9e03d..d515028d6b 100644 --- a/redfish-core/lib/log_services.hpp +++ b/redfish-core/lib/log_services.hpp @@ -21,7 +21,7 @@ #include <systemd/sd-journal.h> #include <boost/container/flat_map.hpp> -#include <boost/utility/string_view.hpp> +#include <error_messages.hpp> #include <variant> namespace redfish @@ -38,6 +38,52 @@ constexpr char const *cpuLogRawPECIInterface = namespace fs = std::filesystem; +using GetManagedPropertyType = boost::container::flat_map< + std::string, + sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t, + int32_t, uint32_t, int64_t, uint64_t, double>>; + +using GetManagedObjectsType = boost::container::flat_map< + sdbusplus::message::object_path, + boost::container::flat_map<std::string, GetManagedPropertyType>>; + +inline std::string translateSeverityDbusToRedfish(const std::string &s) +{ + if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert") + { + return "Critical"; + } + else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") + { + return "Critical"; + } + else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug") + { + return "OK"; + } + else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") + { + return "Critical"; + } + else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error") + { + return "Critical"; + } + else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") + { + return "OK"; + } + else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice") + { + return "OK"; + } + else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning") + { + return "Warning"; + } + return ""; +} + static int getJournalMetadata(sd_journal *journal, const std::string_view &field, std::string_view &contents) @@ -296,7 +342,8 @@ class SystemLogServiceCollection : public Node {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}}); #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG logServiceArray.push_back( - {{"@odata.id", "/redfish/v1/Systems/system/LogServices/CpuLog"}}); + {{ "@odata.id", + "/redfish/v1/Systems/system/LogServices/CpuLog" }}); #endif asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size(); @@ -407,7 +454,7 @@ static int fillEventLogEntryJson(const std::string &bmcLogEntryID, // Fill in the log entry with the gathered data bmcLogEntryJson = { - {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, + {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, {"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + @@ -467,9 +514,10 @@ class EventLogEntryCollection : public Node asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; asyncResp->res.jsonValue["Description"] = "Collection of System Event Log Entries"; + +#ifndef BMCWEB_ENABLE_REDFISH_DBUS_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); @@ -525,6 +573,120 @@ class EventLogEntryCollection : public Node "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" + std::to_string(skip + top); } +#else + // DBus implementation of EventLog/Entries + // Make call to Logging Service to find all log entry objects + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code ec, + GetManagedObjectsType &resp) { + if (ec) + { + // TODO Handle for specific error code + BMCWEB_LOG_ERROR + << "getLogEntriesIfaceData resp_handler got error " + << ec; + messages::internalError(asyncResp->res); + return; + } + nlohmann::json &entriesArray = + asyncResp->res.jsonValue["Members"]; + entriesArray = nlohmann::json::array(); + for (auto &objectPath : resp) + { + for (auto &interfaceMap : objectPath.second) + { + if (interfaceMap.first != + "xyz.openbmc_project.Logging.Entry") + { + BMCWEB_LOG_DEBUG << "Bailing early on " + << interfaceMap.first; + continue; + } + entriesArray.push_back({}); + nlohmann::json &thisEntry = entriesArray.back(); + uint32_t *id; + std::time_t timestamp; + std::string *severity, *message; + bool *resolved; + for (auto &propertyMap : interfaceMap.second) + { + if (propertyMap.first == "Id") + { + id = sdbusplus::message::variant_ns::get_if< + uint32_t>(&propertyMap.second); + if (id == nullptr) + { + messages::propertyMissing(asyncResp->res, + "Id"); + } + } + else if (propertyMap.first == "Timestamp") + { + const uint64_t *millisTimeStamp = + std::get_if<uint64_t>(&propertyMap.second); + if (millisTimeStamp == nullptr) + { + messages::propertyMissing(asyncResp->res, + "Timestamp"); + } + // Retrieve Created property with format: + // yyyy-mm-ddThh:mm:ss + std::chrono::milliseconds chronoTimeStamp( + *millisTimeStamp); + timestamp = + std::chrono::duration_cast< + std::chrono::seconds>(chronoTimeStamp) + .count(); + } + else if (propertyMap.first == "Severity") + { + severity = std::get_if<std::string>( + &propertyMap.second); + if (severity == nullptr) + { + messages::propertyMissing(asyncResp->res, + "Severity"); + } + } + else if (propertyMap.first == "Message") + { + message = std::get_if<std::string>( + &propertyMap.second); + if (message == nullptr) + { + messages::propertyMissing(asyncResp->res, + "Message"); + } + } + } + thisEntry = { + {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, + {"@odata.context", "/redfish/v1/" + "$metadata#LogEntry.LogEntry"}, + {"@odata.id", + "/redfish/v1/Systems/system/LogServices/EventLog/" + "Entries/" + + std::to_string(*id)}, + {"Name", "System DBus Event Log Entry"}, + {"Id", std::to_string(*id)}, + {"Message", *message}, + {"EntryType", "Event"}, + {"Severity", + translateSeverityDbusToRedfish(*severity)}, + {"Created", crow::utility::getDateTime(timestamp)}}; + } + } + std::sort(entriesArray.begin(), entriesArray.end(), + [](const nlohmann::json &left, + const nlohmann::json &right) { + return (left["Id"] <= right["Id"]); + }); + asyncResp->res.jsonValue["Members@odata.count"] = + entriesArray.size(); + }, + "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); +#endif } }; @@ -556,6 +718,8 @@ class EventLogEntry : public Node return; } const std::string &entryID = params[0]; + +#ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES // Convert the unique ID back to a timestamp to find the entry uint64_t ts = 0; uint16_t index = 0; @@ -605,6 +769,91 @@ class EventLogEntry : public Node messages::internalError(asyncResp->res); return; } +#else + // DBus implementation of EventLog/Entries + // Make call to Logging Service to find all log entry objects + crow::connections::systemBus->async_method_call( + [asyncResp, entryID](const boost::system::error_code ec, + GetManagedPropertyType &resp) { + if (ec) + { + BMCWEB_LOG_ERROR + << "EventLogEntry (DBus) resp_handler got error " << ec; + messages::internalError(asyncResp->res); + return; + } + uint32_t *id; + std::time_t timestamp; + std::string *severity, *message; + bool *resolved; + for (auto &propertyMap : resp) + { + if (propertyMap.first == "Id") + { + id = std::get_if<uint32_t>(&propertyMap.second); + if (id == nullptr) + { + messages::propertyMissing(asyncResp->res, "Id"); + } + } + else if (propertyMap.first == "Timestamp") + { + const uint64_t *millisTimeStamp = + std::get_if<uint64_t>(&propertyMap.second); + if (millisTimeStamp == nullptr) + { + messages::propertyMissing(asyncResp->res, + "Timestamp"); + } + // Retrieve Created property with format: + // yyyy-mm-ddThh:mm:ss + std::chrono::milliseconds chronoTimeStamp( + *millisTimeStamp); + timestamp = + std::chrono::duration_cast<std::chrono::seconds>( + chronoTimeStamp) + .count(); + } + else if (propertyMap.first == "Severity") + { + severity = + std::get_if<std::string>(&propertyMap.second); + if (severity == nullptr) + { + messages::propertyMissing(asyncResp->res, + "Severity"); + } + } + else if (propertyMap.first == "Message") + { + message = std::get_if<std::string>(&propertyMap.second); + if (message == nullptr) + { + messages::propertyMissing(asyncResp->res, + "Message"); + } + } + } + asyncResp->res.jsonValue = { + {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, + {"@odata.context", "/redfish/v1/" + "$metadata#LogEntry.LogEntry"}, + {"@odata.id", + "/redfish/v1/Systems/system/LogServices/EventLog/" + "Entries/" + + std::to_string(*id)}, + {"Name", "System DBus Event Log Entry"}, + {"Id", std::to_string(*id)}, + {"Message", *message}, + {"EntryType", "Event"}, + {"Severity", translateSeverityDbusToRedfish(*severity)}, + {"Created", crow::utility::getDateTime(timestamp)}}; + }, + "xyz.openbmc_project.Logging", + "/xyz/openbmc_project/logging/entry/" + entryID, + "org.freedesktop.DBus.Properties", "GetAll", + "xyz.openbmc_project.Logging.Entry"); +#endif } }; @@ -647,7 +896,8 @@ class BMCLogServiceCollection : public Node logServiceArray = nlohmann::json::array(); #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL logServiceArray.push_back( - {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}}); + {{ "@odata.id", + "/redfish/v1/Managers/bmc/LogServices/Journal" }}); #endif asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size(); @@ -724,7 +974,7 @@ static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID, // Fill in the log entry with the gathered data bmcJournalLogEntryJson = { - {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, + {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" + bmcJournalLogEntryID}, @@ -956,8 +1206,9 @@ class CPULogService : public Node #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI asyncResp->res.jsonValue["Actions"]["Oem"].push_back( {"#CpuLog.SendRawPeci", - {{"target", "/redfish/v1/Systems/system/LogServices/CpuLog/" - "Actions/Oem/CpuLog.SendRawPeci"}}}); + { { "target", + "/redfish/v1/Systems/system/LogServices/CpuLog/" + "Actions/Oem/CpuLog.SendRawPeci" } }}); #endif } }; @@ -1118,7 +1369,7 @@ class CPULogEntry : public Node } std::string t = getLogCreatedTime(j); asyncResp->res.jsonValue = { - {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, + {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, {"@odata.id", "/redfish/v1/Systems/system/LogServices/CpuLog/Entries/" + @@ -1231,7 +1482,7 @@ class ImmediateCPULog : public Node } std::string t = getLogCreatedTime(j); asyncResp->res.jsonValue = { - {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, + {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, {"Name", "CPU Debug Log"}, {"EntryType", "Oem"}, @@ -1335,4 +1586,57 @@ class SendRawPECI : public Node } }; +/** + * DBusLogServiceActionsClear class supports POST method for ClearLog action. + */ +class DBusLogServiceActionsClear : public Node +{ + public: + DBusLogServiceActionsClear(CrowApp &app) : + Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/" + "LogService.Reset") + { + 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: + /** + * Function handles POST method request. + * The Clear Log actions does not require any parameter.The action deletes + * all entries found in the Entries collection for this Log Service. + */ + void doPost(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + BMCWEB_LOG_DEBUG << "Do delete all entries."; + + auto asyncResp = std::make_shared<AsyncResp>(res); + // Process response from Logging service. + auto resp_handler = [asyncResp](const boost::system::error_code ec) { + BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done"; + if (ec) + { + // TODO Handle for specific error code + BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec; + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + asyncResp->res.result(boost::beast::http::status::no_content); + }; + + // Make call to Logging service to request Clear Log + crow::connections::systemBus->async_method_call( + resp_handler, "xyz.openbmc_project.Logging", + "/xyz/openbmc_project/logging", + "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); + } +}; } // namespace redfish diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp index 3cde0efe62..e6e80a8a78 100644 --- a/redfish-core/lib/managers.hpp +++ b/redfish-core/lib/managers.hpp @@ -971,7 +971,7 @@ class Manager : public Node manager_reset["ResetType@Redfish.AllowableValues"] = { "GracefulRestart"}; - res.jsonValue["DateTime"] = getDateTime(); + res.jsonValue["DateTime"] = crow::utility::dateTimeNow(); res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1; res.jsonValue["Links"]["ManagerForServers"] = { {{"@odata.id", "/redfish/v1/Systems/system"}}}; @@ -1268,23 +1268,6 @@ class Manager : public Node } } - std::string getDateTime() const - { - std::array<char, 128> dateTime; - std::string redfishDateTime("0000-00-00T00:00:00Z00:00"); - std::time_t time = std::time(nullptr); - - if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z", - std::localtime(&time))) - { - // insert the colon required by the ISO 8601 standard - redfishDateTime = std::string(dateTime.data()); - redfishDateTime.insert(redfishDateTime.end() - 2, ':'); - } - - return redfishDateTime; - } - std::string uuid; }; |