xref: /openbmc/bmcweb/redfish-core/lib/systems_logservices_postcodes.hpp (revision 80e6e25e7d721fa03fcc2b194881d8d8a64fe416)
1 #pragma once
2 
3 #include "app.hpp"
4 #include "dbus_utility.hpp"
5 #include "generated/enums/log_service.hpp"
6 #include "query.hpp"
7 #include "registries/openbmc_message_registry.hpp"
8 #include "registries/privilege_registry.hpp"
9 #include "utils/time_utils.hpp"
10 
11 #include <cstdint>
12 #include <memory>
13 #include <string_view>
14 #include <utility>
15 #include <vector>
16 
17 namespace redfish
18 {
19 
20 inline void handleSystemsLogServicesPostCodesGet(
21     App& app, const crow::Request& req,
22     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
23     const std::string& systemName)
24 {
25     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
26     {
27         return;
28     }
29     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
30     {
31         // Option currently returns no systems.  TBD
32         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
33                                    systemName);
34         return;
35     }
36     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
37     {
38         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
39                                    systemName);
40         return;
41     }
42     asyncResp->res.jsonValue["@odata.id"] =
43         std::format("/redfish/v1/Systems/{}/LogServices/PostCodes",
44                     BMCWEB_REDFISH_SYSTEM_URI_NAME);
45     asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
46     asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
47     asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
48     asyncResp->res.jsonValue["Id"] = "PostCodes";
49     asyncResp->res.jsonValue["OverWritePolicy"] =
50         log_service::OverWritePolicy::WrapsWhenFull;
51     asyncResp->res.jsonValue["Entries"]["@odata.id"] =
52         std::format("/redfish/v1/Systems/{}/LogServices/PostCodes/Entries",
53                     BMCWEB_REDFISH_SYSTEM_URI_NAME);
54 
55     std::pair<std::string, std::string> redfishDateTimeOffset =
56         redfish::time_utils::getDateTimeOffsetNow();
57     asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
58     asyncResp->res.jsonValue["DateTimeLocalOffset"] =
59         redfishDateTimeOffset.second;
60 
61     asyncResp->res
62         .jsonValue["Actions"]["#LogService.ClearLog"]["target"] = std::format(
63         "/redfish/v1/Systems/{}/LogServices/PostCodes/Actions/LogService.ClearLog",
64         BMCWEB_REDFISH_SYSTEM_URI_NAME);
65 }
66 
67 inline void handleSystemsLogServicesPostCodesPost(
68     App& app, const crow::Request& req,
69     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
70     const std::string& systemName)
71 {
72     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
73     {
74         return;
75     }
76     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
77     {
78         // Option currently returns no systems.  TBD
79         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
80                                    systemName);
81         return;
82     }
83     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
84     {
85         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
86                                    systemName);
87         return;
88     }
89     BMCWEB_LOG_DEBUG("Do delete all postcodes entries.");
90 
91     // Make call to post-code service to request clear all
92     crow::connections::systemBus->async_method_call(
93         [asyncResp](const boost::system::error_code& ec) {
94             if (ec)
95             {
96                 // TODO Handle for specific error code
97                 BMCWEB_LOG_ERROR("doClearPostCodes resp_handler got error {}",
98                                  ec);
99                 asyncResp->res.result(
100                     boost::beast::http::status::internal_server_error);
101                 messages::internalError(asyncResp->res);
102                 return;
103             }
104             messages::success(asyncResp->res);
105         },
106         "xyz.openbmc_project.State.Boot.PostCode0",
107         "/xyz/openbmc_project/State/Boot/PostCode0",
108         "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
109 }
110 
111 /**
112  * @brief Parse post code ID and get the current value and index value
113  *        eg: postCodeID=B1-2, currentValue=1, index=2
114  *
115  * @param[in]  postCodeID     Post Code ID
116  * @param[out] currentValue   Current value
117  * @param[out] index          Index value
118  *
119  * @return bool true if the parsing is successful, false the parsing fails
120  */
121 inline bool parsePostCode(std::string_view postCodeID, uint64_t& currentValue,
122                           uint16_t& index)
123 {
124     std::vector<std::string> split;
125     bmcweb::split(split, postCodeID, '-');
126     if (split.size() != 2)
127     {
128         return false;
129     }
130     std::string_view postCodeNumber = split[0];
131     if (postCodeNumber.size() < 2)
132     {
133         return false;
134     }
135     if (postCodeNumber[0] != 'B')
136     {
137         return false;
138     }
139     postCodeNumber.remove_prefix(1);
140     auto [ptrIndex, ecIndex] =
141         std::from_chars(postCodeNumber.begin(), postCodeNumber.end(), index);
142     if (ptrIndex != postCodeNumber.end() || ecIndex != std::errc())
143     {
144         return false;
145     }
146 
147     std::string_view postCodeIndex = split[1];
148 
149     auto [ptrValue, ecValue] = std::from_chars(
150         postCodeIndex.begin(), postCodeIndex.end(), currentValue);
151 
152     return ptrValue == postCodeIndex.end() && ecValue == std::errc();
153 }
154 
155 static bool fillPostCodeEntry(
156     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
157     const boost::container::flat_map<
158         uint64_t, std::tuple<std::vector<uint8_t>, std::vector<uint8_t>>>&
159         postcode,
160     const uint16_t bootIndex, const uint64_t codeIndex = 0,
161     const uint64_t skip = 0, const uint64_t top = 0)
162 {
163     // Get the Message from the MessageRegistry
164     const registries::Message* message =
165         registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
166     if (message == nullptr)
167     {
168         BMCWEB_LOG_ERROR("Couldn't find known message?");
169         return false;
170     }
171     uint64_t currentCodeIndex = 0;
172     uint64_t firstCodeTimeUs = 0;
173     for (const std::pair<uint64_t, std::tuple<std::vector<uint8_t>,
174                                               std::vector<uint8_t>>>& code :
175          postcode)
176     {
177         currentCodeIndex++;
178         std::string postcodeEntryID =
179             "B" + std::to_string(bootIndex) + "-" +
180             std::to_string(currentCodeIndex); // 1 based index in EntryID string
181 
182         uint64_t usecSinceEpoch = code.first;
183         uint64_t usTimeOffset = 0;
184 
185         if (1 == currentCodeIndex)
186         { // already incremented
187             firstCodeTimeUs = code.first;
188         }
189         else
190         {
191             usTimeOffset = code.first - firstCodeTimeUs;
192         }
193 
194         // skip if no specific codeIndex is specified and currentCodeIndex does
195         // not fall between top and skip
196         if ((codeIndex == 0) &&
197             (currentCodeIndex <= skip || currentCodeIndex > top))
198         {
199             continue;
200         }
201 
202         // skip if a specific codeIndex is specified and does not match the
203         // currentIndex
204         if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
205         {
206             // This is done for simplicity. 1st entry is needed to calculate
207             // time offset. To improve efficiency, one can get to the entry
208             // directly (possibly with flatmap's nth method)
209             continue;
210         }
211 
212         // currentCodeIndex is within top and skip or equal to specified code
213         // index
214 
215         // Get the Created time from the timestamp
216         std::string entryTimeStr;
217         entryTimeStr = redfish::time_utils::getDateTimeUintUs(usecSinceEpoch);
218 
219         // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
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 =
231             "0x" + bytesToHexString(std::get<0>(code.second));
232 
233         std::array<std::string_view, 3> messageArgs = {
234             bootIndexStr, timeOffsetString, hexCodeStr};
235 
236         std::string msg =
237             redfish::registries::fillMessageArgs(messageArgs, message->message);
238         if (msg.empty())
239         {
240             messages::internalError(asyncResp->res);
241             return false;
242         }
243 
244         // Get Severity template from message registry
245         std::string severity;
246         if (message != nullptr)
247         {
248             severity = message->messageSeverity;
249         }
250 
251         // Format entry
252         nlohmann::json::object_t bmcLogEntry;
253         bmcLogEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
254         bmcLogEntry["@odata.id"] = boost::urls::format(
255             "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/{}",
256             BMCWEB_REDFISH_SYSTEM_URI_NAME, postcodeEntryID);
257         bmcLogEntry["Name"] = "POST Code Log Entry";
258         bmcLogEntry["Id"] = postcodeEntryID;
259         bmcLogEntry["Message"] = std::move(msg);
260         bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode";
261         bmcLogEntry["MessageArgs"] = messageArgs;
262         bmcLogEntry["EntryType"] = "Event";
263         bmcLogEntry["Severity"] = std::move(severity);
264         bmcLogEntry["Created"] = entryTimeStr;
265         if (!std::get<1>(code.second).empty())
266         {
267             bmcLogEntry["AdditionalDataURI"] =
268                 std::format(
269                     "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/",
270                     BMCWEB_REDFISH_SYSTEM_URI_NAME) +
271                 postcodeEntryID + "/attachment";
272         }
273 
274         // codeIndex is only specified when querying single entry, return only
275         // that entry in this case
276         if (codeIndex != 0)
277         {
278             asyncResp->res.jsonValue.update(bmcLogEntry);
279             return true;
280         }
281 
282         nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
283         logEntryArray.emplace_back(std::move(bmcLogEntry));
284     }
285 
286     // Return value is always false when querying multiple entries
287     return false;
288 }
289 
290 inline void
291     getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
292                         const std::string& entryId)
293 {
294     uint16_t bootIndex = 0;
295     uint64_t codeIndex = 0;
296     if (!parsePostCode(entryId, codeIndex, bootIndex))
297     {
298         // Requested ID was not found
299         messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
300         return;
301     }
302 
303     if (bootIndex == 0 || codeIndex == 0)
304     {
305         // 0 is an invalid index
306         messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
307         return;
308     }
309 
310     crow::connections::systemBus->async_method_call(
311         [asyncResp, entryId, bootIndex,
312          codeIndex](const boost::system::error_code& ec,
313                     const boost::container::flat_map<
314                         uint64_t, std::tuple<std::vector<uint8_t>,
315                                              std::vector<uint8_t>>>& postcode) {
316             if (ec)
317             {
318                 BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error");
319                 messages::internalError(asyncResp->res);
320                 return;
321             }
322 
323             if (postcode.empty())
324             {
325                 messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
326                 return;
327             }
328 
329             if (!fillPostCodeEntry(asyncResp, postcode, bootIndex, codeIndex))
330             {
331                 messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
332                 return;
333             }
334         },
335         "xyz.openbmc_project.State.Boot.PostCode0",
336         "/xyz/openbmc_project/State/Boot/PostCode0",
337         "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
338         bootIndex);
339 }
340 
341 inline void
342     getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
343                        const uint16_t bootIndex, const uint16_t bootCount,
344                        const uint64_t entryCount, size_t skip, size_t top)
345 {
346     crow::connections::systemBus->async_method_call(
347         [asyncResp, bootIndex, bootCount, entryCount, skip,
348          top](const boost::system::error_code& ec,
349               const boost::container::flat_map<
350                   uint64_t, std::tuple<std::vector<uint8_t>,
351                                        std::vector<uint8_t>>>& postcode) {
352             if (ec)
353             {
354                 BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error");
355                 messages::internalError(asyncResp->res);
356                 return;
357             }
358 
359             uint64_t endCount = entryCount;
360             if (!postcode.empty())
361             {
362                 endCount = entryCount + postcode.size();
363                 if (skip < endCount && (top + skip) > entryCount)
364                 {
365                     uint64_t thisBootSkip =
366                         std::max(static_cast<uint64_t>(skip), entryCount) -
367                         entryCount;
368                     uint64_t thisBootTop =
369                         std::min(static_cast<uint64_t>(top + skip), endCount) -
370                         entryCount;
371 
372                     fillPostCodeEntry(asyncResp, postcode, bootIndex, 0,
373                                       thisBootSkip, thisBootTop);
374                 }
375                 asyncResp->res.jsonValue["Members@odata.count"] = endCount;
376             }
377 
378             // continue to previous bootIndex
379             if (bootIndex < bootCount)
380             {
381                 getPostCodeForBoot(asyncResp,
382                                    static_cast<uint16_t>(bootIndex + 1),
383                                    bootCount, endCount, skip, top);
384             }
385             else if (skip + top < endCount)
386             {
387                 asyncResp->res.jsonValue["Members@odata.nextLink"] =
388                     std::format(
389                         "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries?$skip=",
390                         BMCWEB_REDFISH_SYSTEM_URI_NAME) +
391                     std::to_string(skip + top);
392             }
393         },
394         "xyz.openbmc_project.State.Boot.PostCode0",
395         "/xyz/openbmc_project/State/Boot/PostCode0",
396         "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
397         bootIndex);
398 }
399 
400 inline void
401     getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
402                          size_t skip, size_t top)
403 {
404     uint64_t entryCount = 0;
405     dbus::utility::getProperty<uint16_t>(
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 
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 
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<std::vector<uint8_t>,
507                                          std::vector<uint8_t>>>& 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 
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 
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