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