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