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