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