xref: /openbmc/bmcweb/features/redfish/lib/systems_logservices_hostlogger.hpp (revision 40e9b92ec19acffb46f83a6e55b18974da5d708e)
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