Refactor Managers with getValidManagerPath

This adds getValidManagerPath() to find the valid manager dbus path and
its service name. This can be used for manager GET and PATCH operations.

Tested:
- GET /redfish/v1/Managers/bmc
- PATCH /redfish/v1/Managers/ LocationIndicatorActive
- Redfish Service Validator passes

Change-Id: Id46dd12be3050a2097364696a471b188b8167016
Signed-off-by: Myung Bae <myungbae@us.ibm.com>
diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp
index 5932eb9..698f697 100644
--- a/redfish-core/lib/managers.hpp
+++ b/redfish-core/lib/managers.hpp
@@ -41,7 +41,6 @@
 #include <sdbusplus/message/native_types.hpp>
 #include <sdbusplus/unpack_properties.hpp>
 
-#include <array>
 #include <cstddef>
 #include <cstdint>
 #include <format>
@@ -57,44 +56,6 @@
 namespace redfish
 {
 
-inline void handleSetLocationIndicatorActive(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-    bool locationIndicatorActive, const std::string& managerId,
-    const boost::system::error_code& ec,
-    const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths)
-{
-    if (ec)
-    {
-        if (ec == boost::system::errc::io_error)
-        {
-            // Not found
-            BMCWEB_LOG_WARNING("Manager {} not found", managerId);
-            messages::resourceNotFound(asyncResp->res, "Manager", managerId);
-            return;
-        }
-        BMCWEB_LOG_ERROR("D-Bus response error {}", ec.value());
-        messages::internalError(asyncResp->res);
-        return;
-    }
-    if (subtreePaths.empty())
-    {
-        BMCWEB_LOG_WARNING("Manager {} not found", managerId);
-        messages::resourceNotFound(asyncResp->res, "Manager", managerId);
-        return;
-    }
-    // Assume only 1 bmc D-Bus object
-    // Throw an error if there is more than 1
-    if (subtreePaths.size() != 1)
-    {
-        BMCWEB_LOG_ERROR("Found {} Bmc D-Bus paths", subtreePaths.size());
-        messages::internalError(asyncResp->res);
-        return;
-    }
-
-    setLocationIndicatorActive(asyncResp, subtreePaths[0],
-                               locationIndicatorActive);
-}
-
 /**
  * Set the locationIndicatorActive.
  *
@@ -105,13 +66,20 @@
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
     bool locationIndicatorActive, const std::string& managerId)
 {
-    // GetSubTree on all interfaces which provide info about a Manager
-    constexpr std::array<std::string_view, 1> interfaces = {
-        "xyz.openbmc_project.Inventory.Item.Bmc"};
-    dbus::utility::getSubTreePaths(
-        "/xyz/openbmc_project/inventory", 0, interfaces,
-        std::bind_front(handleSetLocationIndicatorActive, asyncResp,
-                        locationIndicatorActive, managerId));
+    manager_utils::getValidManagerPath(
+        asyncResp, managerId,
+        [asyncResp, locationIndicatorActive](
+            const std::string& managerPath,
+            const dbus::utility::MapperServiceMap& serviceMap) {
+            if (managerPath.empty() || serviceMap.size() != 1)
+            {
+                BMCWEB_LOG_DEBUG("Error getting bmc D-Bus object!");
+                messages::internalError(asyncResp->res);
+                return;
+            }
+            setLocationIndicatorActive(asyncResp, managerPath,
+                                       locationIndicatorActive);
+        });
 }
 
 inline std::string getBMCUpdateServiceName()
@@ -651,6 +619,7 @@
 }
 
 inline void getManagerData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                           const std::string& managerId,
                            const std::string& managerPath,
                            const dbus::utility::MapperServiceMap& serviceMap)
 {
@@ -661,102 +630,12 @@
         return;
     }
 
-    for (const auto& [connectionName, interfaces] : serviceMap)
-    {
-        for (const auto& interfaceName : interfaces)
-        {
-            if (interfaceName ==
-                "xyz.openbmc_project.Inventory.Decorator.Asset")
-            {
-                dbus::utility::getAllProperties(
-                    *crow::connections::systemBus, connectionName, managerPath,
-                    "xyz.openbmc_project.Inventory.Decorator.Asset",
-                    std::bind_front(getPhysicalAssets, asyncResp));
-            }
-            else if (interfaceName ==
-                     "xyz.openbmc_project.Inventory.Decorator.LocationCode")
-            {
-                getLocation(asyncResp, connectionName, managerPath);
-            }
-            else if (interfaceName ==
-                     "xyz.openbmc_project.Association.Definitions")
-            {
-                getLocationIndicatorActive(asyncResp, managerPath);
-            }
-        }
-    }
-}
-
-inline void afterGetManagerObject(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-    const boost::system::error_code& ec,
-    const dbus::utility::MapperGetSubTreeResponse& subtree,
-    const std::function<
-        void(const std::string& managerPath,
-             const dbus::utility::MapperServiceMap& serviceMap)>& callback)
-{
-    if (ec)
-    {
-        BMCWEB_LOG_DEBUG("D-Bus response error on GetSubTree {}", ec);
-        return;
-    }
-    if (subtree.empty())
-    {
-        BMCWEB_LOG_DEBUG("Can't find bmc D-Bus object!");
-        return;
-    }
-    // Assume only 1 bmc D-Bus object
-    // Throw an error if there is more than 1
-    if (subtree.size() > 1)
-    {
-        BMCWEB_LOG_ERROR("Found more than 1 bmc D-Bus object!");
-        messages::internalError(asyncResp->res);
-        return;
-    }
-
-    callback(subtree[0].first, subtree[0].second);
-}
-
-inline void getManagerObject(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-    const std::string& /* managerId */,
-    std::function<void(const std::string& managerPath,
-                       const dbus::utility::MapperServiceMap& serviceMap)>&&
-        callback)
-{
-    constexpr std::array<std::string_view, 1> interfaces = {
-        "xyz.openbmc_project.Inventory.Item.Bmc"};
-    dbus::utility::getSubTree(
-        "/xyz/openbmc_project/inventory", 0, interfaces,
-        [asyncResp, callback{std::move(callback)}](
-            const boost::system::error_code& ec,
-            const dbus::utility::MapperGetSubTreeResponse& subtree) {
-            afterGetManagerObject(asyncResp, ec, subtree, callback);
-        });
-}
-
-inline void handleManagerGet(
-    App& app, const crow::Request& req,
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-    const std::string& managerId)
-{
-    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-    {
-        return;
-    }
-
-    if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
-    {
-        messages::resourceNotFound(asyncResp->res, "Manager", managerId);
-        return;
-    }
-
     std::string uuid = persistent_data::getConfig().systemUuid;
 
-    asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
-        "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME);
+    asyncResp->res.jsonValue["@odata.id"] =
+        boost::urls::format("/redfish/v1/Managers/{}", managerId);
     asyncResp->res.jsonValue["@odata.type"] = "#Manager.v1_15_0.Manager";
-    asyncResp->res.jsonValue["Id"] = BMCWEB_REDFISH_MANAGER_URI_NAME;
+    asyncResp->res.jsonValue["Id"] = managerId;
     asyncResp->res.jsonValue["Name"] = "OpenBmc Manager";
     asyncResp->res.jsonValue["Description"] = "Baseboard Management Controller";
     asyncResp->res.jsonValue["PowerState"] = resource::PowerState::On;
@@ -766,14 +645,14 @@
     asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid;
     asyncResp->res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
 
-    asyncResp->res.jsonValue["LogServices"]["@odata.id"] = boost::urls::format(
-        "/redfish/v1/Managers/{}/LogServices", BMCWEB_REDFISH_MANAGER_URI_NAME);
+    asyncResp->res.jsonValue["LogServices"]["@odata.id"] =
+        boost::urls::format("/redfish/v1/Managers/{}/LogServices", managerId);
     asyncResp->res.jsonValue["NetworkProtocol"]["@odata.id"] =
         boost::urls::format("/redfish/v1/Managers/{}/NetworkProtocol",
-                            BMCWEB_REDFISH_MANAGER_URI_NAME);
+                            managerId);
     asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] =
         boost::urls::format("/redfish/v1/Managers/{}/EthernetInterfaces",
-                            BMCWEB_REDFISH_MANAGER_URI_NAME);
+                            managerId);
 
     manager_utils::getServiceIdentification(asyncResp, false);
 
@@ -781,19 +660,17 @@
     {
         asyncResp->res.jsonValue["VirtualMedia"]["@odata.id"] =
             boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia",
-                                BMCWEB_REDFISH_MANAGER_URI_NAME);
+                                managerId);
     }
 
     // Manager.Reset (an action) can be many values, OpenBMC only
     // supports BMC reboot.
     nlohmann::json& managerReset =
         asyncResp->res.jsonValue["Actions"]["#Manager.Reset"];
-    managerReset["target"] =
-        boost::urls::format("/redfish/v1/Managers/{}/Actions/Manager.Reset",
-                            BMCWEB_REDFISH_MANAGER_URI_NAME);
-    managerReset["@Redfish.ActionInfo"] =
-        boost::urls::format("/redfish/v1/Managers/{}/ResetActionInfo",
-                            BMCWEB_REDFISH_MANAGER_URI_NAME);
+    managerReset["target"] = boost::urls::format(
+        "/redfish/v1/Managers/{}/Actions/Manager.Reset", managerId);
+    managerReset["@Redfish.ActionInfo"] = boost::urls::format(
+        "/redfish/v1/Managers/{}/ResetActionInfo", managerId);
 
     // ResetToDefaults (Factory Reset) has values like
     // PreserveNetworkAndUsers and PreserveNetwork that aren't supported
@@ -801,8 +678,7 @@
     nlohmann::json& resetToDefaults =
         asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"];
     resetToDefaults["target"] = boost::urls::format(
-        "/redfish/v1/Managers/{}/Actions/Manager.ResetToDefaults",
-        BMCWEB_REDFISH_MANAGER_URI_NAME);
+        "/redfish/v1/Managers/{}/Actions/Manager.ResetToDefaults", managerId);
     resetToDefaults["ResetType@Redfish.AllowableValues"] =
         nlohmann::json::array_t({"ResetAll"});
 
@@ -844,9 +720,8 @@
     // ManagerDiagnosticData is added for all BMCs.
     nlohmann::json& managerDiagnosticData =
         asyncResp->res.jsonValue["ManagerDiagnosticData"];
-    managerDiagnosticData["@odata.id"] =
-        boost::urls::format("/redfish/v1/Managers/{}/ManagerDiagnosticData",
-                            BMCWEB_REDFISH_MANAGER_URI_NAME);
+    managerDiagnosticData["@odata.id"] = boost::urls::format(
+        "/redfish/v1/Managers/{}/ManagerDiagnosticData", managerId);
 
     getMainChassisId(
         asyncResp, [](const std::string& chassisId,
@@ -885,8 +760,51 @@
             checkForQuiesced(asyncResp);
         });
 
-    getManagerObject(asyncResp, managerId,
-                     std::bind_front(getManagerData, asyncResp));
+    for (const auto& [connectionName, interfaces] : serviceMap)
+    {
+        for (const auto& interfaceName : interfaces)
+        {
+            if (interfaceName ==
+                "xyz.openbmc_project.Inventory.Decorator.Asset")
+            {
+                dbus::utility::getAllProperties(
+                    *crow::connections::systemBus, connectionName, managerPath,
+                    "xyz.openbmc_project.Inventory.Decorator.Asset",
+                    std::bind_front(getPhysicalAssets, asyncResp));
+            }
+            else if (interfaceName ==
+                     "xyz.openbmc_project.Inventory.Decorator.LocationCode")
+            {
+                getLocation(asyncResp, connectionName, managerPath);
+            }
+            else if (interfaceName ==
+                     "xyz.openbmc_project.Association.Definitions")
+            {
+                getLocationIndicatorActive(asyncResp, managerPath);
+            }
+        }
+    }
+}
+
+inline void handleManagerGet(
+    App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& managerId)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+
+    if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
+    {
+        messages::resourceNotFound(asyncResp->res, "Manager", managerId);
+        return;
+    }
+
+    manager_utils::getValidManagerPath(
+        asyncResp, managerId,
+        std::bind_front(getManagerData, asyncResp, managerId));
 
     RedfishService::getInstance(app).handleSubRoute(req, asyncResp);
 }