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