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