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
fillHostLoggerEntryJson(std::string_view logEntryID,std::string_view msg,nlohmann::json::object_t & logEntryJson)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
handleSystemsLogServicesHostloggerGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)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
handleSystemsLogServicesHostloggerEntriesGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)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
handleSystemsLogServicesHostloggerEntriesEntryGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & param)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
requestRoutesSystemsLogServiceHostlogger(App & app)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