diff options
author | Carson Labrado <clabrado@google.com> | 2023-03-27 20:04:46 +0300 |
---|---|---|
committer | Ed Tanous <ed@tanous.net> | 2023-10-20 19:41:26 +0300 |
commit | 168d1b1ac9dac748ea57f7038d0abebfef7462a2 (patch) | |
tree | 4cad2a4a63e425176971782f071cbea8c15b96e7 /redfish-core | |
parent | 04b0e33c15ba4774a3fc914399594340e87dc706 (diff) | |
download | bmcweb-168d1b1ac9dac748ea57f7038d0abebfef7462a2.tar.xz |
LogService: Retrieve dump generated by Manager
Adds support for retrieving the dump file that's generated by
phosphor-debug-collector as a result of using the LogServices/Dump
Action LogService.CollectDiagnosticData from the bmc Manager resource.
Refactors the handling for
/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/attachment
to use one of the new functions and remove the large lambda.
Tested:
I began the dump generation process by sending a POST request to
/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData.
That spawned a Task to track the dump being generated by
phosphor-debug-collector. The dump was retrieved by querying the
/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/attachment URI
which is associated with the Task.
Verified that an event log returned by querying
/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/attachment
is the same as it was before this change.
Signed-off-by: Carson Labrado <clabrado@google.com>
Change-Id: I352b2628a9990bbde40f22e6134f02c89189c925
Diffstat (limited to 'redfish-core')
-rw-r--r-- | redfish-core/include/redfish.hpp | 1 | ||||
-rw-r--r-- | redfish-core/lib/log_services.hpp | 314 |
2 files changed, 207 insertions, 108 deletions
diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp index 3d8dae97a9..540bb070c7 100644 --- a/redfish-core/include/redfish.hpp +++ b/redfish-core/include/redfish.hpp @@ -144,6 +144,7 @@ class RedfishService requestRoutesBMCDumpService(app); requestRoutesBMCDumpEntryCollection(app); requestRoutesBMCDumpEntry(app); + requestRoutesBMCDumpEntryDownload(app); requestRoutesBMCDumpCreate(app); requestRoutesBMCDumpClear(app); diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp index 65942289b7..2899b011f6 100644 --- a/redfish-core/lib/log_services.hpp +++ b/redfish-core/lib/log_services.hpp @@ -701,6 +701,160 @@ inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, "xyz.openbmc_project.Object.Delete", "Delete"); } +inline void + downloadEntryCallback(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& entryID, + const std::string& downloadEntryType, + const boost::system::error_code& ec, + const sdbusplus::message::unix_fd& unixfd) +{ + if (ec.value() == EBADR) + { + messages::resourceNotFound(asyncResp->res, "EntryAttachment", entryID); + return; + } + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error: {}", ec); + messages::internalError(asyncResp->res); + return; + } + + // Make sure we know how to process the retrieved entry attachment + if ((downloadEntryType != "BMC") && (downloadEntryType != "System")) + { + BMCWEB_LOG_ERROR("downloadEntryCallback() invalid entry type: {}", + downloadEntryType); + messages::internalError(asyncResp->res); + } + + int fd = -1; + fd = dup(unixfd); + if (fd < 0) + { + BMCWEB_LOG_ERROR("Failed to open file"); + messages::internalError(asyncResp->res); + return; + } + + long long int size = lseek(fd, 0, SEEK_END); + if (size <= 0) + { + BMCWEB_LOG_ERROR("Failed to get size of file, lseek() returned {}", + size); + messages::internalError(asyncResp->res); + close(fd); + return; + } + + // Arbitrary max size of 20MB to accommodate BMC dumps + constexpr int maxFileSize = 20 * 1024 * 1024; + if (size > maxFileSize) + { + BMCWEB_LOG_ERROR("File size {} exceeds maximum allowed size of {}", + size, maxFileSize); + messages::internalError(asyncResp->res); + close(fd); + return; + } + long long int rc = lseek(fd, 0, SEEK_SET); + if (rc < 0) + { + BMCWEB_LOG_ERROR("Failed to reset file offset to 0"); + messages::internalError(asyncResp->res); + close(fd); + return; + } + + asyncResp->res.body().resize(static_cast<size_t>(size), '\0'); + rc = read(fd, asyncResp->res.body().data(), asyncResp->res.body().size()); + if ((rc == -1) || (rc != size)) + { + BMCWEB_LOG_ERROR("Failed to read in file"); + messages::internalError(asyncResp->res); + close(fd); + return; + } + close(fd); + + if (downloadEntryType == "System") + { + // We need to encode the data. Doing it this way saves the other paths + // from having to make a copy into a temporary variable. + asyncResp->res.body() = + crow::utility::base64encode(asyncResp->res.body()); + asyncResp->res.addHeader( + boost::beast::http::field::content_transfer_encoding, "Base64"); + } + + asyncResp->res.addHeader(boost::beast::http::field::content_type, + "application/octet-stream"); +} + +inline void + downloadDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& entryID, const std::string& dumpType) +{ + if (dumpType != "BMC") + { + BMCWEB_LOG_WARNING("Can't find Dump Entry {}", entryID); + messages::resourceNotFound(asyncResp->res, dumpType + " dump", entryID); + return; + } + + std::string dumpEntryPath = + sdbusplus::message::object_path("/xyz/openbmc_project/dump/") / + std::string(boost::algorithm::to_lower_copy(dumpType)) / "entry" / + entryID; + + auto downloadDumpEntryHandler = + [asyncResp, entryID, + dumpType](const boost::system::error_code& ec, + const sdbusplus::message::unix_fd& unixfd) { + downloadEntryCallback(asyncResp, entryID, dumpType, ec, unixfd); + }; + + crow::connections::systemBus->async_method_call( + std::move(downloadDumpEntryHandler), "xyz.openbmc_project.Dump.Manager", + dumpEntryPath, "xyz.openbmc_project.Dump.Entry", "GetFileHandle"); +} + +inline void + downloadEventLogEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, + const std::string& entryID, + const std::string& dumpType) +{ + if constexpr (bmcwebEnableMultiHost) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != "system") + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + + std::string entryPath = + sdbusplus::message::object_path("/xyz/openbmc_project/logging/entry") / + entryID; + + auto downloadEventLogEntryHandler = + [asyncResp, entryID, + dumpType](const boost::system::error_code& ec, + const sdbusplus::message::unix_fd& unixfd) { + downloadEntryCallback(asyncResp, entryID, dumpType, ec, unixfd); + }; + + crow::connections::systemBus->async_method_call( + std::move(downloadEventLogEntryHandler), "xyz.openbmc_project.Logging", + entryPath, "xyz.openbmc_project.Logging.Entry", "GetEntry"); +} + inline DumpCreationProgress mapDbusStatusToDumpProgress(const std::string& status) { @@ -1960,114 +2114,6 @@ inline void requestRoutesDBusEventLogEntry(App& app) }); } -inline void requestRoutesDBusEventLogEntryDownload(App& app) -{ - BMCWEB_ROUTE( - app, - "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/attachment") - .privileges(redfish::privileges::getLogEntry) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, const std::string& param) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if (!http_helpers::isContentTypeAllowed( - req.getHeaderValue("Accept"), - http_helpers::ContentType::OctetStream, true)) - { - asyncResp->res.result(boost::beast::http::status::bad_request); - return; - } - if constexpr (bmcwebEnableMultiHost) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != "system") - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - - std::string entryID = param; - dbus::utility::escapePathForDbus(entryID); - - crow::connections::systemBus->async_method_call( - [asyncResp, entryID](const boost::system::error_code& ec, - const sdbusplus::message::unix_fd& unixfd) { - if (ec.value() == EBADR) - { - messages::resourceNotFound(asyncResp->res, "EventLogAttachment", - entryID); - return; - } - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error {}", ec); - messages::internalError(asyncResp->res); - return; - } - - int fd = -1; - fd = dup(unixfd); - if (fd == -1) - { - messages::internalError(asyncResp->res); - return; - } - - long long int size = lseek(fd, 0, SEEK_END); - if (size == -1) - { - messages::internalError(asyncResp->res); - return; - } - - // Arbitrary max size of 64kb - constexpr int maxFileSize = 65536; - if (size > maxFileSize) - { - BMCWEB_LOG_ERROR("File size exceeds maximum allowed size of {}", - maxFileSize); - messages::internalError(asyncResp->res); - return; - } - std::vector<char> data(static_cast<size_t>(size)); - long long int rc = lseek(fd, 0, SEEK_SET); - if (rc == -1) - { - messages::internalError(asyncResp->res); - return; - } - rc = read(fd, data.data(), data.size()); - if ((rc == -1) || (rc != size)) - { - messages::internalError(asyncResp->res); - return; - } - close(fd); - - std::string_view strData(data.data(), data.size()); - std::string output = crow::utility::base64encode(strData); - - asyncResp->res.addHeader(boost::beast::http::field::content_type, - "application/octet-stream"); - asyncResp->res.addHeader( - boost::beast::http::field::content_transfer_encoding, "Base64"); - asyncResp->res.body() = std::move(output); - }, - "xyz.openbmc_project.Logging", - "/xyz/openbmc_project/logging/entry/" + entryID, - "xyz.openbmc_project.Logging.Entry", "GetEntry"); - }); -} - constexpr const char* hostLoggerFolderPath = "/var/log/console"; inline bool @@ -2829,6 +2875,7 @@ inline void handleLogServicesDumpEntryGet( } getDumpEntryById(asyncResp, dumpId, dumpType); } + inline void handleLogServicesDumpEntryComputerSystemGet( crow::App& app, const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, @@ -2875,6 +2922,37 @@ inline void handleLogServicesDumpEntryComputerSystemDelete( deleteDumpEntry(asyncResp, dumpId, "System"); } +inline void handleLogServicesDumpEntryDownloadGet( + crow::App& app, const std::string& dumpType, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& dumpId) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + downloadDumpEntry(asyncResp, dumpId, dumpType); +} + +inline void handleDBusEventLogEntryDownloadGet( + crow::App& app, const std::string& dumpType, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, const std::string& entryID) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if (!http_helpers::isContentTypeAllowed( + req.getHeaderValue("Accept"), + http_helpers::ContentType::OctetStream, true)) + { + asyncResp->res.result(boost::beast::http::status::bad_request); + return; + } + downloadEventLogEntry(asyncResp, systemName, entryID, dumpType); +} + inline void handleLogServicesDumpCollectDiagnosticDataPost( crow::App& app, const std::string& dumpType, const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) @@ -2979,6 +3057,16 @@ inline void requestRoutesBMCDumpEntry(App& app) handleLogServicesDumpEntryDelete, std::ref(app), "BMC")); } +inline void requestRoutesBMCDumpEntryDownload(App& app) +{ + BMCWEB_ROUTE( + app, + "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/attachment") + .privileges(redfish::privileges::getLogEntry) + .methods(boost::beast::http::verb::get)(std::bind_front( + handleLogServicesDumpEntryDownloadGet, std::ref(app), "BMC")); +} + inline void requestRoutesBMCDumpCreate(App& app) { BMCWEB_ROUTE( @@ -3000,6 +3088,16 @@ inline void requestRoutesBMCDumpClear(App& app) handleLogServicesDumpClearLogPost, std::ref(app), "BMC")); } +inline void requestRoutesDBusEventLogEntryDownload(App& app) +{ + BMCWEB_ROUTE( + app, + "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/attachment") + .privileges(redfish::privileges::getLogEntry) + .methods(boost::beast::http::verb::get)(std::bind_front( + handleDBusEventLogEntryDownloadGet, std::ref(app), "System")); +} + inline void requestRoutesFaultLogDumpService(App& app) { BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/") |