Implement LocationIndicatorActive for Manager resource

Implement LocationIndicatorActive for Manager schema to set and get the
status of the location LED for the manager.[1] This property was added
to the Manager schema in version v1_11_0.

Uses the utility functions getLocationIndicatorActive() and
setLocationIndicatorActive() to follow the association and get or set
the LED value.

[1] https://redfish.dmtf.org/schemas/v1/Manager.v1_22_0.json

Tested:
- Redfish Service Validator passes
- Tested on p10bmc hardware simulator:
1. Get LocationIndicatorActive
```
curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/Managers/bmc
{
  "@odata.id": "/redfish/v1/Managers/bmc",
  "@odata.type": "#Manager.v1_14_0.Manager",
  ...
  "LocationIndicatorActive": false,
  "LogServices": {
    "@odata.id": "/redfish/v1/Managers/bmc/LogServices"
  },
  ...
}
```

2. Set LocationIndicatorActive to true
```
curl -k -H "X-Auth-Token: $token" -H "Content-Type: application/json" -X PATCH -d '{"LocationIndicatorActive":true}' https://${bmc}/redfish/v1/Managers/bmc
curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/Managers/bmc
{
  "@odata.id": "/redfish/v1/Managers/bmc",
  "@odata.type": "#Manager.v1_14_0.Manager",
  ...
  "LocationIndicatorActive": true,
  ...
}
```

3. Use busctl set-propery to change the value back to false
```
busctl get-property xyz.openbmc_project.LED.GroupManager /xyz/openbmc_project/led/groups/bmc_ingraham_identify xyz.openbmc_project.Led.Group Asserted
b true
busctl set-property xyz.openbmc_project.LED.GroupManager /xyz/openbmc_project/led/groups/bmc_ingraham_identify xyz.openbmc_project.Led.Group Asserted b false
curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/Managers/bmc
{
  "@odata.id": "/redfish/v1/Managers/bmc",
  "@odata.type": "#Manager.v1_14_0.Manager",
  ...
  "LocationIndicatorActive": false,
  ...
}
```

4. Error returned when trying to set the value to non-boolean
```
curl -k -H "X-Auth-Token: $token" -H "Content-Type: application/json" -X PATCH -d '{"LocationIndicatorActive":1}' https://${bmc}/redfish/v1/Managers/bmc
{
  "LocationIndicatorActive@Message.ExtendedInfo": [
    {
      "@odata.type": "#Message.v1_1_1.Message",
      "Message": "The value '1' for the property LocationIndicatorActive is not a type that the property can accept.",
      "MessageArgs": [
        "1",
        "LocationIndicatorActive"
      ],
      "MessageId": "Base.1.19.PropertyValueTypeError",
      "MessageSeverity": "Warning",
      "Resolution": "Correct the value for the property in the request body and resubmit the request if the operation failed."
    }
  ]
}
```

5. Error returned when specifying an unknown Manager resource
```
curl -k -H "X-Auth-Token: $token" -H "Content-Type: application/json" -X PATCH -d '{"LocationIndicatorActive":true}' https://${bmc}/redfish/v1/Managers/bmc2
{
  "error": {
    "@Message.ExtendedInfo": [
      {
        "@odata.type": "#Message.v1_1_1.Message",
        "Message": "The requested resource of type Manager named 'bmc2' was not found.",
        "MessageArgs": [
          "Manager",
          "bmc2"
        ],
        "MessageId": "Base.1.19.ResourceNotFound",
        "MessageSeverity": "Critical",
        "Resolution": "Provide a valid resource identifier and resubmit the request."
      }
    ],
    "code": "Base.1.19.ResourceNotFound",
    "message": "The requested resource of type Manager named 'bmc2' was not found."
  }
}

curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/Managers/bmc2
{
  "error": {
    "@Message.ExtendedInfo": [
      {
        "@odata.type": "#Message.v1_1_1.Message",
        "Message": "The requested resource of type Manager named 'bmc2' was not found.",
        "MessageArgs": [
          "Manager",
          "bmc2"
        ],
        "MessageId": "Base.1.19.ResourceNotFound",
        "MessageSeverity": "Critical",
        "Resolution": "Provide a valid resource identifier and resubmit the request."
      }
    ],
    "code": "Base.1.19.ResourceNotFound",
    "message": "The requested resource of type Manager named 'bmc2' was not found."
  }
}
```

Signed-off-by: George Liu <liuxiwei@inspur.com>
Signed-off-by: Janet Adkins <janeta@us.ibm.com>
Change-Id: I7155e2298962a950491da89e92f8480f98dbeccb
diff --git a/Redfish.md b/Redfish.md
index 0a49af2..252507b 100644
--- a/Redfish.md
+++ b/Redfish.md
@@ -554,6 +554,7 @@
 - Links/ManagerInChassis
 - Links/SoftwareImages
 - Links/SoftwareImages@odata.count
+- LocationIndicatorActive
 - LogServices
 - ManagerType
 - Manufacturer
diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp
index 44308bb..40848f4 100644
--- a/redfish-core/lib/managers.hpp
+++ b/redfish-core/lib/managers.hpp
@@ -14,6 +14,7 @@
 #include "generated/enums/manager.hpp"
 #include "generated/enums/resource.hpp"
 #include "http_request.hpp"
+#include "led.hpp"
 #include "logging.hpp"
 #include "persistent_data.hpp"
 #include "query.hpp"
@@ -56,6 +57,63 @@
 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.
+ *
+ * @param[in,out]   asyncResp                   Async HTTP response.
+ * @param[in]       locationIndicatorActive     Value of the property
+ */
+inline void setLocationIndicatorActiveState(
+    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));
+}
+
 inline std::string getBMCUpdateServiceName()
 {
     if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
@@ -654,6 +712,11 @@
             {
                 getLocation(asyncResp, connectionName, managerPath);
             }
+            else if (interfaceName ==
+                     "xyz.openbmc_project.Association.Definitions")
+            {
+                getLocationIndicatorActive(asyncResp, managerPath);
+            }
         }
     }
 }
@@ -895,6 +958,7 @@
 
                 std::optional<std::string> activeSoftwareImageOdataId;
                 std::optional<std::string> datetime;
+                std::optional<bool> locationIndicatorActive;
                 std::optional<nlohmann::json::object_t> pidControllers;
                 std::optional<nlohmann::json::object_t> fanControllers;
                 std::optional<nlohmann::json::object_t> fanZones;
@@ -906,6 +970,8 @@
                         "DateTime", datetime,                             //
                         "Links/ActiveSoftwareImage/@odata.id",
                         activeSoftwareImageOdataId,                       //
+                        "LocationIndicatorActive",
+                        locationIndicatorActive,                          //
                         "Oem/OpenBmc/Fan/FanControllers", fanControllers, //
                         "Oem/OpenBmc/Fan/FanZones", fanZones,             //
                         "Oem/OpenBmc/Fan/PidControllers", pidControllers, //
@@ -928,6 +994,12 @@
                     setDateTime(asyncResp, *datetime);
                 }
 
+                if (locationIndicatorActive)
+                {
+                    setLocationIndicatorActiveState(
+                        asyncResp, *locationIndicatorActive, managerId);
+                }
+
                 RedfishService::getInstance(app).handleSubRoute(req, asyncResp);
             });
 }