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