LogService: Add support for Fault Log

The corresponding log service is at
/redfish/v1/Managers/bmc/LogServices/FaultLog

Fault Log is a new type of dump for aggregating fault data. For details
please see
https://github.com/openbmc/docs/blob/master/designs/hw-fault-monitor.md

We're currently assuming we'll have a single Fault Log instance per
BMC, which can support multiple host processors.

Tested:
First enabled redfish-dump-log option

Fault Log showed up in the expected LogService collection:
./curl_bin -k -H "X-Auth-Token: $token" -X GET http://${bmc}/redfish/v1/Managers/bmc/LogServices/
{
  "@odata.id": "/redfish/v1/Managers/bmc/LogServices",
  "@odata.type": "#LogServiceCollection.LogServiceCollection",
  "Description": "Collection of LogServices for this Manager",
  "Members": [
    {
      "@odata.id": "/redfish/v1/Managers/bmc/LogServices/Journal"
    },
    {
      "@odata.id": "/redfish/v1/Managers/bmc/LogServices/Dump"
    },
    {
      "@odata.id": "/redfish/v1/Managers/bmc/LogServices/FaultLog"
    }
  ],
  "Members@odata.count": 3,
  "Name": "Open BMC Log Services Collection"
}

FaultLog dump entries are created when a fault is detected by a BMC
daemon -- we don't support FaultLog dump entry creation requested by
a Redfish client via the CollectDiagnosticData LogService action.
Fault Log CollectDiagnosticData returned Not Found (HTTP error
404) as expected:
$curl -k -H "X-Auth-Token: $token" -X POST http://${bmc}/redfish/v1/Managers/bmc/LogServices/FaultLog/Actions/LogService.CollectDiagnosticData -d '{"DiagnosticDataType":"Manager", "OEMDiagnosticDataType":"FaultLog"}'
Not Found

Generated Fault Log dump entries via BMC console:
busctl call xyz.openbmc_project.Dump.Manager  /xyz/openbmc_project/dump/faultlog xyz.openbmc_project.Dump.Create CreateDump a{sv} 0

Retrieved Fault Log dump entries (including with query parameters top and skip):
curl -k -H "X-Auth-Token: $token" -X GET
http://${bmc}/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/1
curl -k -H "X-Auth-Token: $token" -X GET
http://${bmc}/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries
curl -k -H "X-Auth-Token: $token" -X GET
http://${bmc}/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries?\$skip=2
curl -k -H "X-Auth-Token: $token" -X GET http://${bmc}/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries?\$top=3

Deleted Fault Log dump entry:
curl -k -H "X-Auth-Token: $token" -X DELETE http://${bmc}/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/1

Note: The following command to clear the Fault Log will not work until
Fault Log entries have the required D-Bus interface implemented
(xyz.openbmc_project.Dump.Entry.FaultLog):
curl -k -H "X-Auth-Token: $token" -X POST http://${bmc}/redfish/v1/Managers/bmc/LogServices/FaultLog/Actions/LogService.ClearLog

Redfish Service Validator succeeded on the following URIs:
/redfish/v1/Managers/bmc/LogServices
/redfish/v1/Managers/bmc/LogServices/FaultLog
/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries

Also did cursory testing of BMC Dump (/redfish/v1/Managers/bmc/LogServices/Dump/)
and System Dump (/redfish/v1/Systems/system/LogServices/Dump/) for code
paths that were refactored.

Signed-off-by: Claire Weinan <cweinan@google.com>
Change-Id: I80b5444e61263f79e89b10abd556d59af6f17c8c
diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
index b7dd3b3..c961b2d 100644
--- a/redfish-core/include/redfish.hpp
+++ b/redfish-core/include/redfish.hpp
@@ -119,6 +119,11 @@
         requestRoutesBMCDumpEntry(app);
         requestRoutesBMCDumpCreate(app);
         requestRoutesBMCDumpClear(app);
+
+        requestRoutesFaultLogDumpService(app);
+        requestRoutesFaultLogDumpEntryCollection(app);
+        requestRoutesFaultLogDumpEntry(app);
+        requestRoutesFaultLogDumpClear(app);
 #endif
 
 #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp
index be90986..11cdeff 100644
--- a/redfish-core/lib/log_services.hpp
+++ b/redfish-core/lib/log_services.hpp
@@ -325,28 +325,45 @@
     return !redfishLogFiles.empty();
 }
 
+std::string getDumpEntriesPath(const std::string& dumpType)
+{
+    std::string entriesPath;
+
+    if (dumpType == "BMC")
+    {
+        entriesPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
+    }
+    else if (dumpType == "FaultLog")
+    {
+        entriesPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/";
+    }
+    else if (dumpType == "System")
+    {
+        entriesPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
+    }
+    else
+    {
+        BMCWEB_LOG_ERROR << "getDumpEntriesPath() invalid dump type: "
+                         << dumpType;
+    }
+
+    // Returns empty string on error
+    return entriesPath;
+}
+
 inline void
     getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
                            const std::string& dumpType)
 {
-    std::string dumpPath;
-    if (dumpType == "BMC")
+    std::string entriesPath = getDumpEntriesPath(dumpType);
+    if (entriesPath.empty())
     {
-        dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
-    }
-    else if (dumpType == "System")
-    {
-        dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
-    }
-    else
-    {
-        BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
         messages::internalError(asyncResp->res);
         return;
     }
 
     crow::connections::systemBus->async_method_call(
-        [asyncResp, dumpPath,
+        [asyncResp, entriesPath,
          dumpType](const boost::system::error_code ec,
                    dbus::utility::ManagedObjectType& resp) {
         if (ec)
@@ -356,6 +373,20 @@
             return;
         }
 
+        // Remove ending slash
+        std::string odataIdStr = entriesPath;
+        if (!odataIdStr.empty())
+        {
+            odataIdStr.pop_back();
+        }
+
+        asyncResp->res.jsonValue["@odata.type"] =
+            "#LogEntryCollection.LogEntryCollection";
+        asyncResp->res.jsonValue["@odata.id"] = std::move(odataIdStr);
+        asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entries";
+        asyncResp->res.jsonValue["Description"] =
+            "Collection of " + dumpType + " Dump Entries";
+
         nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
         entriesArray = nlohmann::json::array();
         std::string dumpEntryPath =
@@ -453,28 +484,26 @@
             }
 
             thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
-            thisEntry["@odata.id"] = dumpPath + entryID;
+            thisEntry["@odata.id"] = entriesPath + entryID;
             thisEntry["Id"] = entryID;
             thisEntry["EntryType"] = "Event";
             thisEntry["Created"] = crow::utility::getDateTimeUint(timestamp);
             thisEntry["Name"] = dumpType + " Dump Entry";
 
-            thisEntry["AdditionalDataSizeBytes"] = size;
-
             if (dumpType == "BMC")
             {
                 thisEntry["DiagnosticDataType"] = "Manager";
                 thisEntry["AdditionalDataURI"] =
-                    "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
-                    entryID + "/attachment";
+                    entriesPath + entryID + "/attachment";
+                thisEntry["AdditionalDataSizeBytes"] = size;
             }
             else if (dumpType == "System")
             {
                 thisEntry["DiagnosticDataType"] = "OEM";
                 thisEntry["OEMDiagnosticDataType"] = "System";
                 thisEntry["AdditionalDataURI"] =
-                    "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
-                    entryID + "/attachment";
+                    entriesPath + entryID + "/attachment";
+                thisEntry["AdditionalDataSizeBytes"] = size;
             }
             entriesArray.push_back(std::move(thisEntry));
         }
@@ -488,26 +517,17 @@
     getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
                      const std::string& entryID, const std::string& dumpType)
 {
-    std::string dumpPath;
-    if (dumpType == "BMC")
+    std::string entriesPath = getDumpEntriesPath(dumpType);
+    if (entriesPath.empty())
     {
-        dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
-    }
-    else if (dumpType == "System")
-    {
-        dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
-    }
-    else
-    {
-        BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
         messages::internalError(asyncResp->res);
         return;
     }
 
     crow::connections::systemBus->async_method_call(
-        [asyncResp, entryID, dumpPath,
-         dumpType](const boost::system::error_code ec,
-                   dbus::utility::ManagedObjectType& resp) {
+        [asyncResp, entryID, dumpType,
+         entriesPath](const boost::system::error_code ec,
+                      dbus::utility::ManagedObjectType& resp) {
         if (ec)
         {
             BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
@@ -603,29 +623,27 @@
 
             asyncResp->res.jsonValue["@odata.type"] =
                 "#LogEntry.v1_8_0.LogEntry";
-            asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
+            asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID;
             asyncResp->res.jsonValue["Id"] = entryID;
             asyncResp->res.jsonValue["EntryType"] = "Event";
             asyncResp->res.jsonValue["Created"] =
                 crow::utility::getDateTimeUint(timestamp);
             asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
 
-            asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
-
             if (dumpType == "BMC")
             {
                 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
                 asyncResp->res.jsonValue["AdditionalDataURI"] =
-                    "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
-                    entryID + "/attachment";
+                    entriesPath + entryID + "/attachment";
+                asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
             }
             else if (dumpType == "System")
             {
                 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
                 asyncResp->res.jsonValue["OEMDiagnosticDataType"] = "System";
                 asyncResp->res.jsonValue["AdditionalDataURI"] =
-                    "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
-                    entryID + "/attachment";
+                    entriesPath + entryID + "/attachment";
+                asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
             }
         }
         if (!foundDumpEntry)
@@ -654,7 +672,7 @@
                 return;
             }
             BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
-                             << ec;
+                             << ec << " entryID=" << entryID;
             messages::internalError(asyncResp->res);
             return;
         }
@@ -719,18 +737,9 @@
 inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
                        const crow::Request& req, const std::string& dumpType)
 {
-    std::string dumpPath;
-    if (dumpType == "BMC")
+    std::string dumpPath = getDumpEntriesPath(dumpType);
+    if (dumpPath.empty())
     {
-        dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
-    }
-    else if (dumpType == "System")
-    {
-        dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
-    }
-    else
-    {
-        BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
         messages::internalError(asyncResp->res);
         return;
     }
@@ -1980,39 +1989,88 @@
         });
 }
 
+constexpr char const* dumpManagerIface =
+    "xyz.openbmc_project.Collection.DeleteAll";
+inline void handleLogServicesCollectionGet(
+    crow::App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    // Collections don't include the static data added by SubRoute
+    // because it has a duplicate entry for members
+    asyncResp->res.jsonValue["@odata.type"] =
+        "#LogServiceCollection.LogServiceCollection";
+    asyncResp->res.jsonValue["@odata.id"] =
+        "/redfish/v1/Managers/bmc/LogServices";
+    asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
+    asyncResp->res.jsonValue["Description"] =
+        "Collection of LogServices for this Manager";
+    nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
+    logServiceArray = nlohmann::json::array();
+
+#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
+    logServiceArray.push_back(
+        {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
+#endif
+
+    asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
+
+#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
+    auto respHandler =
+        [asyncResp](
+            const boost::system::error_code ec,
+            const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
+        if (ec)
+        {
+            BMCWEB_LOG_ERROR
+                << "handleLogServicesCollectionGet respHandler got error "
+                << ec;
+            // Assume that getting an error simply means there are no dump
+            // LogServices. Return without adding any error response.
+            return;
+        }
+
+        nlohmann::json& logServiceArrayLocal =
+            asyncResp->res.jsonValue["Members"];
+
+        for (const std::string& path : subTreePaths)
+        {
+            if (path == "/xyz/openbmc_project/dump/bmc")
+            {
+                logServiceArrayLocal.push_back(
+                    {{"@odata.id",
+                      "/redfish/v1/Managers/bmc/LogServices/Dump"}});
+            }
+            else if (path == "/xyz/openbmc_project/dump/faultlog")
+            {
+                logServiceArrayLocal.push_back(
+                    {{"@odata.id",
+                      "/redfish/v1/Managers/bmc/LogServices/FaultLog"}});
+            }
+        }
+
+        asyncResp->res.jsonValue["Members@odata.count"] =
+            logServiceArrayLocal.size();
+    };
+
+    crow::connections::systemBus->async_method_call(
+        respHandler, "xyz.openbmc_project.ObjectMapper",
+        "/xyz/openbmc_project/object_mapper",
+        "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
+        "/xyz/openbmc_project/dump", 0,
+        std::array<const char*, 1>{dumpManagerIface});
+#endif
+}
+
 inline void requestRoutesBMCLogServiceCollection(App& app)
 {
     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
         .privileges(redfish::privileges::getLogServiceCollection)
         .methods(boost::beast::http::verb::get)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
-        if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-        {
-            return;
-        }
-        // Collections don't include the static data added by SubRoute
-        // because it has a duplicate entry for members
-        asyncResp->res.jsonValue["@odata.type"] =
-            "#LogServiceCollection.LogServiceCollection";
-        asyncResp->res.jsonValue["@odata.id"] =
-            "/redfish/v1/Managers/bmc/LogServices";
-        asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
-        asyncResp->res.jsonValue["Description"] =
-            "Collection of LogServices for this Manager";
-        nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
-        logServiceArray = nlohmann::json::array();
-#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
-        logServiceArray.push_back(
-            {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump"}});
-#endif
-#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
-        logServiceArray.push_back(
-            {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
-#endif
-        asyncResp->res.jsonValue["Members@odata.count"] =
-            logServiceArray.size();
-        });
+            std::bind_front(handleLogServicesCollectionGet, std::ref(app)));
 }
 
 inline void requestRoutesBMCJournalLogService(App& app)
@@ -2266,67 +2324,147 @@
         });
 }
 
+inline void
+    getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                       const std::string& dumpType)
+{
+    std::string dumpPath;
+    std::string overWritePolicy;
+    bool collectDiagnosticDataSupported = false;
+
+    if (dumpType == "BMC")
+    {
+        dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump";
+        overWritePolicy = "WrapsWhenFull";
+        collectDiagnosticDataSupported = true;
+    }
+    else if (dumpType == "FaultLog")
+    {
+        dumpPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog";
+        overWritePolicy = "Unknown";
+        collectDiagnosticDataSupported = false;
+    }
+    else if (dumpType == "System")
+    {
+        dumpPath = "/redfish/v1/Systems/system/LogServices/Dump";
+        overWritePolicy = "WrapsWhenFull";
+        collectDiagnosticDataSupported = true;
+    }
+    else
+    {
+        BMCWEB_LOG_ERROR << "getDumpServiceInfo() invalid dump type: "
+                         << dumpType;
+        messages::internalError(asyncResp->res);
+        return;
+    }
+
+    asyncResp->res.jsonValue["@odata.id"] = dumpPath;
+    asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
+    asyncResp->res.jsonValue["Name"] = "Dump LogService";
+    asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
+    asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
+    asyncResp->res.jsonValue["OverWritePolicy"] = std::move(overWritePolicy);
+
+    std::pair<std::string, std::string> redfishDateTimeOffset =
+        crow::utility::getDateTimeOffsetNow();
+    asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
+    asyncResp->res.jsonValue["DateTimeLocalOffset"] =
+        redfishDateTimeOffset.second;
+
+    asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
+    asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
+        dumpPath + "/Actions/LogService.ClearLog";
+
+    if (collectDiagnosticDataSupported)
+    {
+        asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
+                                ["target"] =
+            dumpPath + "/Actions/LogService.CollectDiagnosticData";
+    }
+}
+
+inline void handleLogServicesDumpServiceGet(
+    crow::App& app, const std::string& dumpType, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    getDumpServiceInfo(asyncResp, dumpType);
+}
+
+inline void handleLogServicesDumpEntriesCollectionGet(
+    crow::App& app, const std::string& dumpType, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    getDumpEntryCollection(asyncResp, dumpType);
+}
+
+inline void handleLogServicesDumpEntryGet(
+    crow::App& app, const std::string& dumpType, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& dumpId)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    getDumpEntryById(asyncResp, dumpId, dumpType);
+}
+
+inline void handleLogServicesDumpEntryDelete(
+    crow::App& app, const std::string& dumpType, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& dumpId)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    deleteDumpEntry(asyncResp, dumpId, dumpType);
+}
+
+inline void handleLogServicesDumpCollectDiagnosticDataPost(
+    crow::App& app, const std::string& dumpType, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    createDump(asyncResp, req, dumpType);
+}
+
+inline void handleLogServicesDumpClearLogPost(
+    crow::App& app, const std::string& dumpType, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    clearDump(asyncResp, dumpType);
+}
+
 inline void requestRoutesBMCDumpService(App& app)
 {
     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
         .privileges(redfish::privileges::getLogService)
-        .methods(boost::beast::http::verb::get)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
-        if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-        {
-            return;
-        }
-        asyncResp->res.jsonValue["@odata.id"] =
-            "/redfish/v1/Managers/bmc/LogServices/Dump";
-        asyncResp->res.jsonValue["@odata.type"] =
-            "#LogService.v1_2_0.LogService";
-        asyncResp->res.jsonValue["Name"] = "Dump LogService";
-        asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
-        asyncResp->res.jsonValue["Id"] = "Dump";
-        asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
-
-        std::pair<std::string, std::string> redfishDateTimeOffset =
-            crow::utility::getDateTimeOffsetNow();
-        asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
-        asyncResp->res.jsonValue["DateTimeLocalOffset"] =
-            redfishDateTimeOffset.second;
-
-        asyncResp->res.jsonValue["Entries"]["@odata.id"] =
-            "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
-        asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
-            "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog";
-        asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
-                                ["target"] =
-            "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData";
-        });
+        .methods(boost::beast::http::verb::get)(std::bind_front(
+            handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
 }
 
 inline void requestRoutesBMCDumpEntryCollection(App& app)
 {
-
-    /**
-     * Functions triggers appropriate requests on DBus
-     */
     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
         .privileges(redfish::privileges::getLogEntryCollection)
-        .methods(boost::beast::http::verb::get)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
-        if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-        {
-            return;
-        }
-        asyncResp->res.jsonValue["@odata.type"] =
-            "#LogEntryCollection.LogEntryCollection";
-        asyncResp->res.jsonValue["@odata.id"] =
-            "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
-        asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
-        asyncResp->res.jsonValue["Description"] =
-            "Collection of BMC Dump Entries";
-
-        getDumpEntryCollection(asyncResp, "BMC");
-        });
+        .methods(boost::beast::http::verb::get)(std::bind_front(
+            handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
 }
 
 inline void requestRoutesBMCDumpEntry(App& app)
@@ -2334,29 +2472,14 @@
     BMCWEB_ROUTE(app,
                  "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
         .privileges(redfish::privileges::getLogEntry)
-        .methods(boost::beast::http::verb::get)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                   const std::string& param) {
-        if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-        {
-            return;
-        }
-        getDumpEntryById(asyncResp, param, "BMC");
-        });
+        .methods(boost::beast::http::verb::get)(std::bind_front(
+            handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
+
     BMCWEB_ROUTE(app,
                  "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
         .privileges(redfish::privileges::deleteLogEntry)
-        .methods(boost::beast::http::verb::delete_)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                   const std::string& param) {
-        if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-        {
-            return;
-        }
-        deleteDumpEntry(asyncResp, param, "bmc");
-        });
+        .methods(boost::beast::http::verb::delete_)(std::bind_front(
+            handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
 }
 
 inline void requestRoutesBMCDumpCreate(App& app)
@@ -2366,14 +2489,8 @@
         "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
         .privileges(redfish::privileges::postLogService)
         .methods(boost::beast::http::verb::post)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
-        if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-        {
-            return;
-        }
-        createDump(asyncResp, req, "BMC");
-        });
+            std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
+                            std::ref(app), "BMC"));
 }
 
 inline void requestRoutesBMCDumpClear(App& app)
@@ -2382,15 +2499,50 @@
         app,
         "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
         .privileges(redfish::privileges::postLogService)
-        .methods(boost::beast::http::verb::post)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
-        if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-        {
-            return;
-        }
-        clearDump(asyncResp, "BMC");
-        });
+        .methods(boost::beast::http::verb::post)(std::bind_front(
+            handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
+}
+
+inline void requestRoutesFaultLogDumpService(App& app)
+{
+    BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/")
+        .privileges(redfish::privileges::getLogService)
+        .methods(boost::beast::http::verb::get)(std::bind_front(
+            handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
+}
+
+inline void requestRoutesFaultLogDumpEntryCollection(App& app)
+{
+    BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/")
+        .privileges(redfish::privileges::getLogEntryCollection)
+        .methods(boost::beast::http::verb::get)(
+            std::bind_front(handleLogServicesDumpEntriesCollectionGet,
+                            std::ref(app), "FaultLog"));
+}
+
+inline void requestRoutesFaultLogDumpEntry(App& app)
+{
+    BMCWEB_ROUTE(app,
+                 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
+        .privileges(redfish::privileges::getLogEntry)
+        .methods(boost::beast::http::verb::get)(std::bind_front(
+            handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
+
+    BMCWEB_ROUTE(app,
+                 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
+        .privileges(redfish::privileges::deleteLogEntry)
+        .methods(boost::beast::http::verb::delete_)(std::bind_front(
+            handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
+}
+
+inline void requestRoutesFaultLogDumpClear(App& app)
+{
+    BMCWEB_ROUTE(
+        app,
+        "/redfish/v1/Managers/bmc/LogServices/FaultLog/Actions/LogService.ClearLog/")
+        .privileges(redfish::privileges::postLogService)
+        .methods(boost::beast::http::verb::post)(std::bind_front(
+            handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
 }
 
 inline void requestRoutesSystemDumpService(App& app)
@@ -2445,14 +2597,6 @@
         {
             return;
         }
-        asyncResp->res.jsonValue["@odata.type"] =
-            "#LogEntryCollection.LogEntryCollection";
-        asyncResp->res.jsonValue["@odata.id"] =
-            "/redfish/v1/Systems/system/LogServices/Dump/Entries";
-        asyncResp->res.jsonValue["Name"] = "System Dump Entries";
-        asyncResp->res.jsonValue["Description"] =
-            "Collection of System Dump Entries";
-
         getDumpEntryCollection(asyncResp, "System");
         });
 }