1e21126ecSEd Tanous #pragma once 2e21126ecSEd Tanous 3e21126ecSEd Tanous #include "app.hpp" 4e21126ecSEd Tanous #include "generated/enums/log_entry.hpp" 5e21126ecSEd Tanous #include "query.hpp" 6e21126ecSEd Tanous #include "registries/openbmc_message_registry.hpp" 7e21126ecSEd Tanous #include "registries/privilege_registry.hpp" 8e21126ecSEd Tanous #include "utils/time_utils.hpp" 9e21126ecSEd Tanous 10e21126ecSEd Tanous #include <cstdint> 11e21126ecSEd Tanous #include <memory> 12e21126ecSEd Tanous #include <string_view> 13e21126ecSEd Tanous #include <utility> 14e21126ecSEd Tanous #include <vector> 15e21126ecSEd Tanous 16e21126ecSEd Tanous namespace redfish 17e21126ecSEd Tanous { 18e21126ecSEd Tanous 19e21126ecSEd Tanous inline void fillHostLoggerEntryJson(std::string_view logEntryID, 20e21126ecSEd Tanous std::string_view msg, 21e21126ecSEd Tanous nlohmann::json::object_t& logEntryJson) 22e21126ecSEd Tanous { 23e21126ecSEd Tanous // Fill in the log entry with the gathered data. 24e21126ecSEd Tanous logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; 25e21126ecSEd Tanous logEntryJson["@odata.id"] = boost::urls::format( 26e21126ecSEd Tanous "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries/{}", 27e21126ecSEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME, logEntryID); 28e21126ecSEd Tanous logEntryJson["Name"] = "Host Logger Entry"; 29e21126ecSEd Tanous logEntryJson["Id"] = logEntryID; 30e21126ecSEd Tanous logEntryJson["Message"] = msg; 31e21126ecSEd Tanous logEntryJson["EntryType"] = log_entry::LogEntryType::Oem; 32e21126ecSEd Tanous logEntryJson["Severity"] = log_entry::EventSeverity::OK; 33e21126ecSEd Tanous logEntryJson["OemRecordFormat"] = "Host Logger Entry"; 34e21126ecSEd Tanous } 35e21126ecSEd Tanous 36*7945eeedSEd Tanous inline void handleSystemsLogServicesHostloggerGet( 37*7945eeedSEd Tanous App& app, const crow::Request& req, 38e21126ecSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 39*7945eeedSEd Tanous const std::string& systemName) 40*7945eeedSEd Tanous { 41e21126ecSEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 42e21126ecSEd Tanous { 43e21126ecSEd Tanous return; 44e21126ecSEd Tanous } 45e21126ecSEd Tanous if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 46e21126ecSEd Tanous { 47e21126ecSEd Tanous // Option currently returns no systems. TBD 48e21126ecSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 49e21126ecSEd Tanous systemName); 50e21126ecSEd Tanous return; 51e21126ecSEd Tanous } 52e21126ecSEd Tanous if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 53e21126ecSEd Tanous { 54e21126ecSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 55e21126ecSEd Tanous systemName); 56e21126ecSEd Tanous return; 57e21126ecSEd Tanous } 58e21126ecSEd Tanous asyncResp->res.jsonValue["@odata.id"] = 59e21126ecSEd Tanous std::format("/redfish/v1/Systems/{}/LogServices/HostLogger", 60e21126ecSEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME); 61*7945eeedSEd Tanous asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; 62e21126ecSEd Tanous asyncResp->res.jsonValue["Name"] = "Host Logger Service"; 63e21126ecSEd Tanous asyncResp->res.jsonValue["Description"] = "Host Logger Service"; 64e21126ecSEd Tanous asyncResp->res.jsonValue["Id"] = "HostLogger"; 65*7945eeedSEd Tanous asyncResp->res.jsonValue["Entries"]["@odata.id"] = 66*7945eeedSEd Tanous std::format("/redfish/v1/Systems/{}/LogServices/HostLogger/Entries", 67e21126ecSEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME); 68e21126ecSEd Tanous } 69e21126ecSEd Tanous 70*7945eeedSEd Tanous inline void handleSystemsLogServicesHostloggerEntriesGet( 71*7945eeedSEd Tanous App& app, const crow::Request& req, 72e21126ecSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 73*7945eeedSEd Tanous const std::string& systemName) 74*7945eeedSEd Tanous { 75e21126ecSEd Tanous query_param::QueryCapabilities capabilities = { 76e21126ecSEd Tanous .canDelegateTop = true, 77e21126ecSEd Tanous .canDelegateSkip = true, 78e21126ecSEd Tanous }; 79e21126ecSEd Tanous query_param::Query delegatedQuery; 80*7945eeedSEd Tanous if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp, 81*7945eeedSEd Tanous delegatedQuery, capabilities)) 82e21126ecSEd Tanous { 83e21126ecSEd Tanous return; 84e21126ecSEd Tanous } 85e21126ecSEd Tanous if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 86e21126ecSEd Tanous { 87e21126ecSEd Tanous // Option currently returns no systems. TBD 88e21126ecSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 89e21126ecSEd Tanous systemName); 90e21126ecSEd Tanous return; 91e21126ecSEd Tanous } 92e21126ecSEd Tanous if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 93e21126ecSEd Tanous { 94e21126ecSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 95e21126ecSEd Tanous systemName); 96e21126ecSEd Tanous return; 97e21126ecSEd Tanous } 98*7945eeedSEd Tanous asyncResp->res.jsonValue["@odata.id"] = 99*7945eeedSEd Tanous std::format("/redfish/v1/Systems/{}/LogServices/HostLogger/Entries", 100e21126ecSEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME); 101e21126ecSEd Tanous asyncResp->res.jsonValue["@odata.type"] = 102e21126ecSEd Tanous "#LogEntryCollection.LogEntryCollection"; 103e21126ecSEd Tanous asyncResp->res.jsonValue["Name"] = "HostLogger Entries"; 104e21126ecSEd Tanous asyncResp->res.jsonValue["Description"] = 105e21126ecSEd Tanous "Collection of HostLogger Entries"; 106e21126ecSEd Tanous nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"]; 107e21126ecSEd Tanous logEntryArray = nlohmann::json::array(); 108e21126ecSEd Tanous asyncResp->res.jsonValue["Members@odata.count"] = 0; 109e21126ecSEd Tanous 110e21126ecSEd Tanous std::vector<std::filesystem::path> hostLoggerFiles; 111e21126ecSEd Tanous if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles)) 112e21126ecSEd Tanous { 113e21126ecSEd Tanous BMCWEB_LOG_DEBUG("Failed to get host log file path"); 114e21126ecSEd Tanous return; 115e21126ecSEd Tanous } 116e21126ecSEd Tanous // If we weren't provided top and skip limits, use the defaults. 117e21126ecSEd Tanous size_t skip = delegatedQuery.skip.value_or(0); 118*7945eeedSEd Tanous size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); 119e21126ecSEd Tanous size_t logCount = 0; 120e21126ecSEd Tanous // This vector only store the entries we want to expose that 121e21126ecSEd Tanous // control by skip and top. 122e21126ecSEd Tanous std::vector<std::string> logEntries; 123*7945eeedSEd Tanous if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries, logCount)) 124e21126ecSEd Tanous { 125e21126ecSEd Tanous messages::internalError(asyncResp->res); 126e21126ecSEd Tanous return; 127e21126ecSEd Tanous } 128e21126ecSEd Tanous // If vector is empty, that means skip value larger than total 129e21126ecSEd Tanous // log count 130e21126ecSEd Tanous if (logEntries.empty()) 131e21126ecSEd Tanous { 132e21126ecSEd Tanous asyncResp->res.jsonValue["Members@odata.count"] = logCount; 133e21126ecSEd Tanous return; 134e21126ecSEd Tanous } 135e21126ecSEd Tanous if (!logEntries.empty()) 136e21126ecSEd Tanous { 137e21126ecSEd Tanous for (size_t i = 0; i < logEntries.size(); i++) 138e21126ecSEd Tanous { 139e21126ecSEd Tanous nlohmann::json::object_t hostLogEntry; 140*7945eeedSEd Tanous fillHostLoggerEntryJson(std::to_string(skip + i), logEntries[i], 141*7945eeedSEd Tanous hostLogEntry); 142e21126ecSEd Tanous logEntryArray.emplace_back(std::move(hostLogEntry)); 143e21126ecSEd Tanous } 144e21126ecSEd Tanous 145e21126ecSEd Tanous asyncResp->res.jsonValue["Members@odata.count"] = logCount; 146e21126ecSEd Tanous if (skip + top < logCount) 147e21126ecSEd Tanous { 148e21126ecSEd Tanous asyncResp->res.jsonValue["Members@odata.nextLink"] = 149e21126ecSEd Tanous std::format( 150e21126ecSEd Tanous "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries?$skip=", 151e21126ecSEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME) + 152e21126ecSEd Tanous std::to_string(skip + top); 153e21126ecSEd Tanous } 154e21126ecSEd Tanous } 155e21126ecSEd Tanous } 156e21126ecSEd Tanous 157*7945eeedSEd Tanous inline void handleSystemsLogServicesHostloggerEntriesEntryGet( 158*7945eeedSEd Tanous App& app, const crow::Request& req, 159e21126ecSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 160*7945eeedSEd Tanous const std::string& systemName, const std::string& param) 161*7945eeedSEd Tanous { 162e21126ecSEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 163e21126ecSEd Tanous { 164e21126ecSEd Tanous return; 165e21126ecSEd Tanous } 166e21126ecSEd Tanous if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 167e21126ecSEd Tanous { 168e21126ecSEd Tanous // Option currently returns no systems. TBD 169e21126ecSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 170e21126ecSEd Tanous systemName); 171e21126ecSEd Tanous return; 172e21126ecSEd Tanous } 173e21126ecSEd Tanous if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 174e21126ecSEd Tanous { 175e21126ecSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 176e21126ecSEd Tanous systemName); 177e21126ecSEd Tanous return; 178e21126ecSEd Tanous } 179e21126ecSEd Tanous std::string_view targetID = param; 180e21126ecSEd Tanous 181e21126ecSEd Tanous uint64_t idInt = 0; 182e21126ecSEd Tanous 183*7945eeedSEd Tanous auto [ptr, ec] = std::from_chars(targetID.begin(), targetID.end(), idInt); 184e21126ecSEd Tanous if (ec != std::errc{} || ptr != targetID.end()) 185e21126ecSEd Tanous { 186*7945eeedSEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", param); 187e21126ecSEd Tanous return; 188e21126ecSEd Tanous } 189e21126ecSEd Tanous 190e21126ecSEd Tanous std::vector<std::filesystem::path> hostLoggerFiles; 191e21126ecSEd Tanous if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles)) 192e21126ecSEd Tanous { 193e21126ecSEd Tanous BMCWEB_LOG_DEBUG("Failed to get host log file path"); 194e21126ecSEd Tanous return; 195e21126ecSEd Tanous } 196e21126ecSEd Tanous 197e21126ecSEd Tanous size_t logCount = 0; 198e21126ecSEd Tanous size_t top = 1; 199e21126ecSEd Tanous std::vector<std::string> logEntries; 200e21126ecSEd Tanous // We can get specific entry by skip and top. For example, if we 201e21126ecSEd Tanous // want to get nth entry, we can set skip = n-1 and top = 1 to 202e21126ecSEd Tanous // get that entry 203*7945eeedSEd Tanous if (!getHostLoggerEntries(hostLoggerFiles, idInt, top, logEntries, 204*7945eeedSEd Tanous logCount)) 205e21126ecSEd Tanous { 206e21126ecSEd Tanous messages::internalError(asyncResp->res); 207e21126ecSEd Tanous return; 208e21126ecSEd Tanous } 209e21126ecSEd Tanous 210e21126ecSEd Tanous if (!logEntries.empty()) 211e21126ecSEd Tanous { 212e21126ecSEd Tanous nlohmann::json::object_t hostLogEntry; 213*7945eeedSEd Tanous fillHostLoggerEntryJson(targetID, logEntries[0], hostLogEntry); 214e21126ecSEd Tanous asyncResp->res.jsonValue.update(hostLogEntry); 215e21126ecSEd Tanous return; 216e21126ecSEd Tanous } 217e21126ecSEd Tanous 218e21126ecSEd Tanous // Requested ID was not found 219e21126ecSEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", param); 220e21126ecSEd Tanous } 221e21126ecSEd Tanous 222e21126ecSEd Tanous inline void requestRoutesSystemsLogServiceHostlogger(App& app) 223e21126ecSEd Tanous { 224*7945eeedSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/HostLogger/") 225*7945eeedSEd Tanous .privileges(redfish::privileges::getLogService) 226*7945eeedSEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 227*7945eeedSEd Tanous handleSystemsLogServicesHostloggerGet, std::ref(app))); 228*7945eeedSEd Tanous BMCWEB_ROUTE(app, 229*7945eeedSEd Tanous "/redfish/v1/Systems/<str>/LogServices/HostLogger/Entries/") 230*7945eeedSEd Tanous .privileges(redfish::privileges::getLogEntry) 231*7945eeedSEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 232*7945eeedSEd Tanous handleSystemsLogServicesHostloggerEntriesGet, std::ref(app))); 233*7945eeedSEd Tanous 234*7945eeedSEd Tanous BMCWEB_ROUTE( 235*7945eeedSEd Tanous app, "/redfish/v1/Systems/<str>/LogServices/HostLogger/Entries/<str>/") 236*7945eeedSEd Tanous .privileges(redfish::privileges::getLogEntry) 237*7945eeedSEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 238*7945eeedSEd Tanous handleSystemsLogServicesHostloggerEntriesEntryGet, std::ref(app))); 239e21126ecSEd Tanous } 240e21126ecSEd Tanous 241e21126ecSEd Tanous } // namespace redfish 242