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