Redfish: Creation of a BMC dump

This commit supports creation of a BMC dump entry.

After initiation of the dump creation, a task is
created that listens to "InterfaceAdded" signal over
"/xyz/openbmc_project/dump" path. Once the task is
completed, it returns the created BMC dump entry ID,
as a part of the task's message args as well as in
the task's http header.

Tested-By:

* curl -k -H "X-Auth-Token: $bmc_token" -X POST
https://${bmc}/redfish/v1/Managers/bmc/LogServices/
Dump/Actions/Oem/OemLogService.CollectDiagnosticData
-d '{"DiagnosticDataType" : "Managers",
"OEMDiagnosticDataType": ""}'

* curl -k -H "X-Auth-Token: $bmc_token" -X POST
https://${bmc}/redfish/v1/Systems/system/LogServices/
Dump/Actions/Oem/OemLogService.CollectDiagnosticData
-d '{"DiagnosticDataType" : "OEM", "OEMDiagnosticDataType":
"System"}'

<Returns a Task>

* curl -k -H "X-Auth-Token: $bmc_token" -X GET
https://${bmc}/redfish/v1/TaskService/Tasks/<task-id>

<Returns Dump ID on completion>

Signed-off-by: Asmitha Karunanithi <asmitk01@in.ibm.com>
Change-Id: Ide44b6abda797d738851123f06696558bab05ce0
diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
index 5dee66b..bf7b38b 100644
--- a/redfish-core/include/redfish.hpp
+++ b/redfish-core/include/redfish.hpp
@@ -108,13 +108,14 @@
         nodes.emplace_back(std::make_unique<SystemDumpService>(app));
         nodes.emplace_back(std::make_unique<SystemDumpEntryCollection>(app));
         nodes.emplace_back(std::make_unique<SystemDumpEntry>(app));
+        nodes.emplace_back(std::make_unique<SystemDumpCreate>(app));
         nodes.emplace_back(std::make_unique<SystemDumpEntryDownload>(app));
         nodes.emplace_back(std::make_unique<SystemDumpClear>(app));
 
         nodes.emplace_back(std::make_unique<BMCDumpService>(app));
         nodes.emplace_back(std::make_unique<BMCDumpEntryCollection>(app));
         nodes.emplace_back(std::make_unique<BMCDumpEntry>(app));
-
+        nodes.emplace_back(std::make_unique<BMCDumpCreate>(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 22c3ad7..1643c3a 100644
--- a/redfish-core/lib/log_services.hpp
+++ b/redfish-core/lib/log_services.hpp
@@ -739,6 +739,143 @@
         "xyz.openbmc_project.Object.Delete", "Delete");
 }
 
+inline void createDumpTaskCallback(const crow::Request& req,
+                                   std::shared_ptr<AsyncResp> asyncResp,
+                                   const uint32_t& dumpId,
+                                   const std::string& dumpPath,
+                                   const std::string& dumpType)
+{
+    std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
+        [dumpId, dumpPath, dumpType](
+            boost::system::error_code err, sdbusplus::message::message& m,
+            const std::shared_ptr<task::TaskData>& taskData) {
+            std::vector<std::pair<
+                std::string,
+                std::vector<std::pair<std::string, std::variant<std::string>>>>>
+                interfacesList;
+
+            sdbusplus::message::object_path objPath;
+
+            m.read(objPath, interfacesList);
+
+            for (auto& interface : interfacesList)
+            {
+                if (interface.first ==
+                    ("xyz.openbmc_project.Dump.Entry." + dumpType))
+                {
+                    nlohmann::json retMessage = messages::success();
+                    taskData->messages.emplace_back(retMessage);
+
+                    std::string headerLoc =
+                        "Location: " + dumpPath + std::to_string(dumpId);
+                    taskData->payload->httpHeaders.emplace_back(
+                        std::move(headerLoc));
+
+                    taskData->state = "Completed";
+                    return task::completed;
+                }
+            }
+            return !task::completed;
+        },
+        "type='signal',interface='org.freedesktop.DBus."
+        "ObjectManager',"
+        "member='InterfacesAdded', "
+        "path='/xyz/openbmc_project/dump'");
+
+    task->startTimer(std::chrono::minutes(3));
+    task->populateResp(asyncResp->res);
+    task->payload.emplace(req);
+}
+
+inline void createDump(crow::Response& res, const crow::Request& req,
+                       const std::string& dumpType)
+{
+    std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
+
+    std::string dumpPath;
+    if (dumpType == "BMC")
+    {
+        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;
+    }
+
+    std::optional<std::string> diagnosticDataType;
+    std::optional<std::string> oemDiagnosticDataType;
+
+    if (!redfish::json_util::readJson(
+            req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
+            "OEMDiagnosticDataType", oemDiagnosticDataType))
+    {
+        return;
+    }
+
+    if (dumpType == "System")
+    {
+        if (!oemDiagnosticDataType || !diagnosticDataType)
+        {
+            BMCWEB_LOG_ERROR << "CreateDump action parameter "
+                                "'DiagnosticDataType'/"
+                                "'OEMDiagnosticDataType' value not found!";
+            messages::actionParameterMissing(
+                asyncResp->res, "CollectDiagnosticData",
+                "DiagnosticDataType & OEMDiagnosticDataType");
+            return;
+        }
+        else if ((*oemDiagnosticDataType != "System") ||
+                 (*diagnosticDataType != "OEM"))
+        {
+            BMCWEB_LOG_ERROR << "Wrong parameter values passed";
+            messages::invalidObject(asyncResp->res,
+                                    "System Dump creation parameters");
+            return;
+        }
+    }
+    else if (dumpType == "BMC")
+    {
+        if (!diagnosticDataType)
+        {
+            BMCWEB_LOG_ERROR << "CreateDump action parameter "
+                                "'DiagnosticDataType' not found!";
+            messages::actionParameterMissing(
+                asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
+            return;
+        }
+        else if (*diagnosticDataType != "Manager")
+        {
+            BMCWEB_LOG_ERROR
+                << "Wrong parameter value passed for 'DiagnosticDataType'";
+            messages::invalidObject(asyncResp->res,
+                                    "BMC Dump creation parameters");
+            return;
+        }
+    }
+
+    crow::connections::systemBus->async_method_call(
+        [asyncResp, req, dumpPath, dumpType](const boost::system::error_code ec,
+                                             const uint32_t& dumpId) {
+            if (ec)
+            {
+                BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
+                messages::internalError(asyncResp->res);
+                return;
+            }
+            BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
+
+            createDumpTaskCallback(req, asyncResp, dumpId, dumpPath, dumpType);
+        },
+        "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
+        "xyz.openbmc_project.Dump.Create", "CreateDump");
+}
+
 static void ParseCrashdumpParameters(
     const std::vector<std::pair<std::string, VariantType>>& params,
     std::string& filename, std::string& timestamp, std::string& logfile)
@@ -1971,6 +2108,31 @@
     }
 };
 
+class BMCDumpCreate : public Node
+{
+  public:
+    BMCDumpCreate(CrowApp& app) :
+        Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
+                  "Actions/Oem/"
+                  "OemLogService.CollectDiagnosticData/")
+    {
+        entityPrivileges = {
+            {boost::beast::http::verb::get, {{"Login"}}},
+            {boost::beast::http::verb::head, {{"Login"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
+            {boost::beast::http::verb::put, {{"ConfigureManager"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
+            {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
+    }
+
+  private:
+    void doPost(crow::Response& res, const crow::Request& req,
+                const std::vector<std::string>& params) override
+    {
+        createDump(res, req, "BMC");
+    }
+};
+
 class SystemDumpService : public Node
 {
   public:
@@ -2095,6 +2257,31 @@
     }
 };
 
+class SystemDumpCreate : public Node
+{
+  public:
+    SystemDumpCreate(CrowApp& app) :
+        Node(app, "/redfish/v1/Systems/system/LogServices/Dump/"
+                  "Actions/Oem/"
+                  "OemLogService.CollectDiagnosticData/")
+    {
+        entityPrivileges = {
+            {boost::beast::http::verb::get, {{"Login"}}},
+            {boost::beast::http::verb::head, {{"Login"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
+            {boost::beast::http::verb::put, {{"ConfigureManager"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
+            {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
+    }
+
+  private:
+    void doPost(crow::Response& res, const crow::Request& req,
+                const std::vector<std::string>& params) override
+    {
+        createDump(res, req, "System");
+    }
+};
+
 class SystemDumpEntryDownload : public Node
 {
   public: