1 #pragma once
2
3 #include "app.hpp"
4 #include "generated/enums/log_service.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
handleSystemsLogServicesPostCodesGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)19 inline void handleSystemsLogServicesPostCodesGet(
20 App& app, const crow::Request& req,
21 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
22 const std::string& systemName)
23 {
24 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
25 {
26 return;
27 }
28 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
29 {
30 // Option currently returns no systems. TBD
31 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
32 systemName);
33 return;
34 }
35 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
36 {
37 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
38 systemName);
39 return;
40 }
41 asyncResp->res.jsonValue["@odata.id"] =
42 std::format("/redfish/v1/Systems/{}/LogServices/PostCodes",
43 BMCWEB_REDFISH_SYSTEM_URI_NAME);
44 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
45 asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
46 asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
47 asyncResp->res.jsonValue["Id"] = "PostCodes";
48 asyncResp->res.jsonValue["OverWritePolicy"] =
49 log_service::OverWritePolicy::WrapsWhenFull;
50 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
51 std::format("/redfish/v1/Systems/{}/LogServices/PostCodes/Entries",
52 BMCWEB_REDFISH_SYSTEM_URI_NAME);
53
54 std::pair<std::string, std::string> redfishDateTimeOffset =
55 redfish::time_utils::getDateTimeOffsetNow();
56 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
57 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
58 redfishDateTimeOffset.second;
59
60 asyncResp->res
61 .jsonValue["Actions"]["#LogService.ClearLog"]["target"] = std::format(
62 "/redfish/v1/Systems/{}/LogServices/PostCodes/Actions/LogService.ClearLog",
63 BMCWEB_REDFISH_SYSTEM_URI_NAME);
64 }
65
handleSystemsLogServicesPostCodesPost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)66 inline void handleSystemsLogServicesPostCodesPost(
67 App& app, const crow::Request& req,
68 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
69 const std::string& systemName)
70 {
71 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
72 {
73 return;
74 }
75 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
76 {
77 // Option currently returns no systems. TBD
78 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
79 systemName);
80 return;
81 }
82 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
83 {
84 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
85 systemName);
86 return;
87 }
88 BMCWEB_LOG_DEBUG("Do delete all postcodes entries.");
89
90 // Make call to post-code service to request clear all
91 crow::connections::systemBus->async_method_call(
92 [asyncResp](const boost::system::error_code& ec) {
93 if (ec)
94 {
95 // TODO Handle for specific error code
96 BMCWEB_LOG_ERROR("doClearPostCodes resp_handler got error {}",
97 ec);
98 asyncResp->res.result(
99 boost::beast::http::status::internal_server_error);
100 messages::internalError(asyncResp->res);
101 return;
102 }
103 messages::success(asyncResp->res);
104 },
105 "xyz.openbmc_project.State.Boot.PostCode0",
106 "/xyz/openbmc_project/State/Boot/PostCode0",
107 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
108 }
109
110 /**
111 * @brief Parse post code ID and get the current value and index value
112 * eg: postCodeID=B1-2, currentValue=1, index=2
113 *
114 * @param[in] postCodeID Post Code ID
115 * @param[out] currentValue Current value
116 * @param[out] index Index value
117 *
118 * @return bool true if the parsing is successful, false the parsing fails
119 */
parsePostCode(std::string_view postCodeID,uint64_t & currentValue,uint16_t & index)120 inline bool parsePostCode(std::string_view postCodeID, uint64_t& currentValue,
121 uint16_t& index)
122 {
123 std::vector<std::string> split;
124 bmcweb::split(split, postCodeID, '-');
125 if (split.size() != 2)
126 {
127 return false;
128 }
129 std::string_view postCodeNumber = split[0];
130 if (postCodeNumber.size() < 2)
131 {
132 return false;
133 }
134 if (postCodeNumber[0] != 'B')
135 {
136 return false;
137 }
138 postCodeNumber.remove_prefix(1);
139 auto [ptrIndex, ecIndex] =
140 std::from_chars(postCodeNumber.begin(), postCodeNumber.end(), index);
141 if (ptrIndex != postCodeNumber.end() || ecIndex != std::errc())
142 {
143 return false;
144 }
145
146 std::string_view postCodeIndex = split[1];
147
148 auto [ptrValue, ecValue] = std::from_chars(
149 postCodeIndex.begin(), postCodeIndex.end(), currentValue);
150
151 return ptrValue == postCodeIndex.end() && ecValue == std::errc();
152 }
153
fillPostCodeEntry(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::container::flat_map<uint64_t,std::tuple<uint64_t,std::vector<uint8_t>>> & postcode,const uint16_t bootIndex,const uint64_t codeIndex=0,const uint64_t skip=0,const uint64_t top=0)154 static bool fillPostCodeEntry(
155 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
156 const boost::container::flat_map<
157 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
158 const uint16_t bootIndex, const uint64_t codeIndex = 0,
159 const uint64_t skip = 0, const uint64_t top = 0)
160 {
161 // Get the Message from the MessageRegistry
162 const registries::Message* message =
163 registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
164 if (message == nullptr)
165 {
166 BMCWEB_LOG_ERROR("Couldn't find known message?");
167 return false;
168 }
169 uint64_t currentCodeIndex = 0;
170 uint64_t firstCodeTimeUs = 0;
171 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
172 code : postcode)
173 {
174 currentCodeIndex++;
175 std::string postcodeEntryID =
176 "B" + std::to_string(bootIndex) + "-" +
177 std::to_string(currentCodeIndex); // 1 based index in EntryID string
178
179 uint64_t usecSinceEpoch = code.first;
180 uint64_t usTimeOffset = 0;
181
182 if (1 == currentCodeIndex)
183 { // already incremented
184 firstCodeTimeUs = code.first;
185 }
186 else
187 {
188 usTimeOffset = code.first - firstCodeTimeUs;
189 }
190
191 // skip if no specific codeIndex is specified and currentCodeIndex does
192 // not fall between top and skip
193 if ((codeIndex == 0) &&
194 (currentCodeIndex <= skip || currentCodeIndex > top))
195 {
196 continue;
197 }
198
199 // skip if a specific codeIndex is specified and does not match the
200 // currentIndex
201 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
202 {
203 // This is done for simplicity. 1st entry is needed to calculate
204 // time offset. To improve efficiency, one can get to the entry
205 // directly (possibly with flatmap's nth method)
206 continue;
207 }
208
209 // currentCodeIndex is within top and skip or equal to specified code
210 // index
211
212 // Get the Created time from the timestamp
213 std::string entryTimeStr;
214 entryTimeStr = redfish::time_utils::getDateTimeUintUs(usecSinceEpoch);
215
216 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
217 std::ostringstream hexCode;
218 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
219 << std::get<0>(code.second);
220 std::ostringstream timeOffsetStr;
221 // Set Fixed -Point Notation
222 timeOffsetStr << std::fixed;
223 // Set precision to 4 digits
224 timeOffsetStr << std::setprecision(4);
225 // Add double to stream
226 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
227
228 std::string bootIndexStr = std::to_string(bootIndex);
229 std::string timeOffsetString = timeOffsetStr.str();
230 std::string hexCodeStr = hexCode.str();
231
232 std::array<std::string_view, 3> messageArgs = {
233 bootIndexStr, timeOffsetString, hexCodeStr};
234
235 std::string msg =
236 redfish::registries::fillMessageArgs(messageArgs, message->message);
237 if (msg.empty())
238 {
239 messages::internalError(asyncResp->res);
240 return false;
241 }
242
243 // Get Severity template from message registry
244 std::string severity;
245 if (message != nullptr)
246 {
247 severity = message->messageSeverity;
248 }
249
250 // Format entry
251 nlohmann::json::object_t bmcLogEntry;
252 bmcLogEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
253 bmcLogEntry["@odata.id"] = boost::urls::format(
254 "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/{}",
255 BMCWEB_REDFISH_SYSTEM_URI_NAME, postcodeEntryID);
256 bmcLogEntry["Name"] = "POST Code Log Entry";
257 bmcLogEntry["Id"] = postcodeEntryID;
258 bmcLogEntry["Message"] = std::move(msg);
259 bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode";
260 bmcLogEntry["MessageArgs"] = messageArgs;
261 bmcLogEntry["EntryType"] = "Event";
262 bmcLogEntry["Severity"] = std::move(severity);
263 bmcLogEntry["Created"] = entryTimeStr;
264 if (!std::get<std::vector<uint8_t>>(code.second).empty())
265 {
266 bmcLogEntry["AdditionalDataURI"] =
267 std::format(
268 "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/",
269 BMCWEB_REDFISH_SYSTEM_URI_NAME) +
270 postcodeEntryID + "/attachment";
271 }
272
273 // codeIndex is only specified when querying single entry, return only
274 // that entry in this case
275 if (codeIndex != 0)
276 {
277 asyncResp->res.jsonValue.update(bmcLogEntry);
278 return true;
279 }
280
281 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
282 logEntryArray.emplace_back(std::move(bmcLogEntry));
283 }
284
285 // Return value is always false when querying multiple entries
286 return false;
287 }
288
289 inline void
getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryId)290 getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
291 const std::string& entryId)
292 {
293 uint16_t bootIndex = 0;
294 uint64_t codeIndex = 0;
295 if (!parsePostCode(entryId, codeIndex, bootIndex))
296 {
297 // Requested ID was not found
298 messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
299 return;
300 }
301
302 if (bootIndex == 0 || codeIndex == 0)
303 {
304 // 0 is an invalid index
305 messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
306 return;
307 }
308
309 crow::connections::systemBus->async_method_call(
310 [asyncResp, entryId, bootIndex,
311 codeIndex](const boost::system::error_code& ec,
312 const boost::container::flat_map<
313 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
314 postcode) {
315 if (ec)
316 {
317 BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error");
318 messages::internalError(asyncResp->res);
319 return;
320 }
321
322 if (postcode.empty())
323 {
324 messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
325 return;
326 }
327
328 if (!fillPostCodeEntry(asyncResp, postcode, bootIndex, codeIndex))
329 {
330 messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
331 return;
332 }
333 },
334 "xyz.openbmc_project.State.Boot.PostCode0",
335 "/xyz/openbmc_project/State/Boot/PostCode0",
336 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
337 bootIndex);
338 }
339
340 inline void
getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const uint16_t bootIndex,const uint16_t bootCount,const uint64_t entryCount,size_t skip,size_t top)341 getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
342 const uint16_t bootIndex, const uint16_t bootCount,
343 const uint64_t entryCount, size_t skip, size_t top)
344 {
345 crow::connections::systemBus->async_method_call(
346 [asyncResp, bootIndex, bootCount, entryCount, skip,
347 top](const boost::system::error_code& ec,
348 const boost::container::flat_map<
349 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
350 postcode) {
351 if (ec)
352 {
353 BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error");
354 messages::internalError(asyncResp->res);
355 return;
356 }
357
358 uint64_t endCount = entryCount;
359 if (!postcode.empty())
360 {
361 endCount = entryCount + postcode.size();
362 if (skip < endCount && (top + skip) > entryCount)
363 {
364 uint64_t thisBootSkip =
365 std::max(static_cast<uint64_t>(skip), entryCount) -
366 entryCount;
367 uint64_t thisBootTop =
368 std::min(static_cast<uint64_t>(top + skip), endCount) -
369 entryCount;
370
371 fillPostCodeEntry(asyncResp, postcode, bootIndex, 0,
372 thisBootSkip, thisBootTop);
373 }
374 asyncResp->res.jsonValue["Members@odata.count"] = endCount;
375 }
376
377 // continue to previous bootIndex
378 if (bootIndex < bootCount)
379 {
380 getPostCodeForBoot(asyncResp,
381 static_cast<uint16_t>(bootIndex + 1),
382 bootCount, endCount, skip, top);
383 }
384 else if (skip + top < endCount)
385 {
386 asyncResp->res.jsonValue["Members@odata.nextLink"] =
387 std::format(
388 "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries?$skip=",
389 BMCWEB_REDFISH_SYSTEM_URI_NAME) +
390 std::to_string(skip + top);
391 }
392 },
393 "xyz.openbmc_project.State.Boot.PostCode0",
394 "/xyz/openbmc_project/State/Boot/PostCode0",
395 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
396 bootIndex);
397 }
398
399 inline void
getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,size_t skip,size_t top)400 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
401 size_t skip, size_t top)
402 {
403 uint64_t entryCount = 0;
404 sdbusplus::asio::getProperty<uint16_t>(
405 *crow::connections::systemBus,
406 "xyz.openbmc_project.State.Boot.PostCode0",
407 "/xyz/openbmc_project/State/Boot/PostCode0",
408 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
409 [asyncResp, entryCount, skip,
410 top](const boost::system::error_code& ec, const uint16_t bootCount) {
411 if (ec)
412 {
413 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
414 messages::internalError(asyncResp->res);
415 return;
416 }
417 getPostCodeForBoot(asyncResp, 1, bootCount, entryCount, skip, top);
418 });
419 }
420
handleSystemsLogServicesPostCodesEntriesGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)421 inline void handleSystemsLogServicesPostCodesEntriesGet(
422 App& app, const crow::Request& req,
423 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
424 const std::string& systemName)
425 {
426 query_param::QueryCapabilities capabilities = {
427 .canDelegateTop = true,
428 .canDelegateSkip = true,
429 };
430 query_param::Query delegatedQuery;
431 if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp,
432 delegatedQuery, capabilities))
433 {
434 return;
435 }
436 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
437 {
438 // Option currently returns no systems. TBD
439 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
440 systemName);
441 return;
442 }
443
444 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
445 {
446 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
447 systemName);
448 return;
449 }
450 asyncResp->res.jsonValue["@odata.type"] =
451 "#LogEntryCollection.LogEntryCollection";
452 asyncResp->res.jsonValue["@odata.id"] =
453 std::format("/redfish/v1/Systems/{}/LogServices/PostCodes/Entries",
454 BMCWEB_REDFISH_SYSTEM_URI_NAME);
455 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
456 asyncResp->res.jsonValue["Description"] =
457 "Collection of POST Code Log Entries";
458 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
459 asyncResp->res.jsonValue["Members@odata.count"] = 0;
460 size_t skip = delegatedQuery.skip.value_or(0);
461 size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
462 getCurrentBootNumber(asyncResp, skip, top);
463 }
464
handleSystemsLogServicesPostCodesEntriesEntryAdditionalDataGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & postCodeID)465 inline void handleSystemsLogServicesPostCodesEntriesEntryAdditionalDataGet(
466 App& app, const crow::Request& req,
467 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
468 const std::string& systemName, const std::string& postCodeID)
469 {
470 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
471 {
472 return;
473 }
474 if (!http_helpers::isContentTypeAllowed(
475 req.getHeaderValue("Accept"),
476 http_helpers::ContentType::OctetStream, true))
477 {
478 asyncResp->res.result(boost::beast::http::status::bad_request);
479 return;
480 }
481 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
482 {
483 // Option currently returns no systems. TBD
484 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
485 systemName);
486 return;
487 }
488 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
489 {
490 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
491 systemName);
492 return;
493 }
494
495 uint64_t currentValue = 0;
496 uint16_t index = 0;
497 if (!parsePostCode(postCodeID, currentValue, index))
498 {
499 messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID);
500 return;
501 }
502
503 crow::connections::systemBus->async_method_call(
504 [asyncResp, postCodeID, currentValue](
505 const boost::system::error_code& ec,
506 const std::vector<std::tuple<uint64_t, std::vector<uint8_t>>>&
507 postcodes) {
508 if (ec.value() == EBADR)
509 {
510 messages::resourceNotFound(asyncResp->res, "LogEntry",
511 postCodeID);
512 return;
513 }
514 if (ec)
515 {
516 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
517 messages::internalError(asyncResp->res);
518 return;
519 }
520
521 size_t value = static_cast<size_t>(currentValue) - 1;
522 if (value == std::string::npos || postcodes.size() < currentValue)
523 {
524 BMCWEB_LOG_WARNING("Wrong currentValue value");
525 messages::resourceNotFound(asyncResp->res, "LogEntry",
526 postCodeID);
527 return;
528 }
529
530 const auto& [tID, c] = postcodes[value];
531 if (c.empty())
532 {
533 BMCWEB_LOG_WARNING("No found post code data");
534 messages::resourceNotFound(asyncResp->res, "LogEntry",
535 postCodeID);
536 return;
537 }
538 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
539 const char* d = reinterpret_cast<const char*>(c.data());
540 std::string_view strData(d, c.size());
541
542 asyncResp->res.addHeader(boost::beast::http::field::content_type,
543 "application/octet-stream");
544 asyncResp->res.addHeader(
545 boost::beast::http::field::content_transfer_encoding, "Base64");
546 asyncResp->res.write(crow::utility::base64encode(strData));
547 },
548 "xyz.openbmc_project.State.Boot.PostCode0",
549 "/xyz/openbmc_project/State/Boot/PostCode0",
550 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index);
551 }
552
handleSystemsLogServicesPostCodesEntriesEntryGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & targetID)553 inline void handleSystemsLogServicesPostCodesEntriesEntryGet(
554 App& app, const crow::Request& req,
555 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
556 const std::string& systemName, const std::string& targetID)
557 {
558 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
559 {
560 return;
561 }
562 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
563 {
564 // Option currently returns no systems. TBD
565 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
566 systemName);
567 return;
568 }
569 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
570 {
571 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
572 systemName);
573 return;
574 }
575
576 getPostCodeForEntry(asyncResp, targetID);
577 }
578
requestRoutesSystemsLogServicesPostCode(App & app)579 inline void requestRoutesSystemsLogServicesPostCode(App& app)
580 {
581 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/")
582 .privileges(redfish::privileges::getLogService)
583 .methods(boost::beast::http::verb::get)(std::bind_front(
584 handleSystemsLogServicesPostCodesGet, std::ref(app)));
585
586 BMCWEB_ROUTE(
587 app,
588 "/redfish/v1/Systems/<str>/LogServices/PostCodes/Actions/LogService.ClearLog/")
589 // The following privilege is correct; we need "SubordinateOverrides"
590 // before we can automate it.
591 .privileges({{"ConfigureComponents"}})
592 .methods(boost::beast::http::verb::post)(std::bind_front(
593 handleSystemsLogServicesPostCodesPost, std::ref(app)));
594
595 BMCWEB_ROUTE(app,
596 "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/")
597 .privileges(redfish::privileges::getLogEntryCollection)
598 .methods(boost::beast::http::verb::get)(std::bind_front(
599 handleSystemsLogServicesPostCodesEntriesGet, std::ref(app)));
600
601 BMCWEB_ROUTE(
602 app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/")
603 .privileges(redfish::privileges::getLogEntry)
604 .methods(boost::beast::http::verb::get)(std::bind_front(
605 handleSystemsLogServicesPostCodesEntriesEntryGet, std::ref(app)));
606
607 BMCWEB_ROUTE(
608 app,
609 "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/attachment/")
610 .privileges(redfish::privileges::getLogEntry)
611 .methods(boost::beast::http::verb::get)(std::bind_front(
612 handleSystemsLogServicesPostCodesEntriesEntryAdditionalDataGet,
613 std::ref(app)));
614 }
615 } // namespace redfish
616