Add IndicatorLed to Chassis Schema

This adds the indicator property to Chassis Schema,
and moves the logic from systems.hpp to a common header
to share the code.

Tested: Passed the validator, was able to turn LED on

Change-Id: I79458a2a4656d7ddf2939bb9f56845eb6d9a27ca
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp
index 84acc58..59987fb 100644
--- a/redfish-core/lib/chassis.hpp
+++ b/redfish-core/lib/chassis.hpp
@@ -16,6 +16,7 @@
 #pragma once
 
 #include "health.hpp"
+#include "led.hpp"
 #include "node.hpp"
 
 #include <boost/container/flat_map.hpp>
@@ -264,10 +265,7 @@
         crow::connections::systemBus->async_method_call(
             [asyncResp, chassisId(std::string(chassisId))](
                 const boost::system::error_code ec,
-                const std::vector<std::pair<
-                    std::string, std::vector<std::pair<
-                                     std::string, std::vector<std::string>>>>>
-                    &subtree) {
+                const crow::openbmc_mapper::GetSubTreeType &subtree) {
                 if (ec)
                 {
                     messages::internalError(asyncResp->res);
@@ -316,9 +314,7 @@
 
                     if (connectionNames.size() < 1)
                     {
-                        BMCWEB_LOG_ERROR << "Only got "
-                                         << connectionNames.size()
-                                         << " Connection names";
+                        BMCWEB_LOG_ERROR << "Got 0 Connection names";
                         continue;
                     }
 
@@ -336,6 +332,23 @@
 
                     const std::string &connectionName =
                         connectionNames[0].first;
+
+                    const std::vector<std::string> &interfaces =
+                        connectionNames[0].second;
+                    const std::array<const char *, 2> hasIndicatorLed = {
+                        "xyz.openbmc_project.Inventory.Item.Panel",
+                        "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
+
+                    for (const char *interface : hasIndicatorLed)
+                    {
+                        if (std::find(interfaces.begin(), interfaces.end(),
+                                      interface) != interfaces.end())
+                        {
+                            getIndicatorLedState(asyncResp);
+                            break;
+                        }
+                    }
+
                     crow::connections::systemBus->async_method_call(
                         [asyncResp, chassisId(std::string(chassisId))](
                             const boost::system::error_code ec,
@@ -404,5 +417,107 @@
 
         getPhysicalSecurityData(asyncResp);
     }
+
+    void doPatch(crow::Response &res, const crow::Request &req,
+                 const std::vector<std::string> &params) override
+    {
+        std::optional<std::string> indicatorLed;
+        auto asyncResp = std::make_shared<AsyncResp>(res);
+
+        if (params.size() != 1)
+        {
+            return;
+        }
+
+        if (!json_util::readJson(req, res, "IndicatorLED", indicatorLed))
+        {
+            return;
+        }
+
+        if (!indicatorLed)
+        {
+            return; // delete this when we support more patch properties
+        }
+
+        const std::array<const char *, 2> interfaces = {
+            "xyz.openbmc_project.Inventory.Item.Board",
+            "xyz.openbmc_project.Inventory.Item.Chassis"};
+
+        const std::string &chassisId = params[0];
+
+        crow::connections::systemBus->async_method_call(
+            [asyncResp, chassisId, indicatorLed](
+                const boost::system::error_code ec,
+                const crow::openbmc_mapper::GetSubTreeType &subtree) {
+                if (ec)
+                {
+                    messages::internalError(asyncResp->res);
+                    return;
+                }
+
+                // Iterate over all retrieved ObjectPaths.
+                for (const std::pair<
+                         std::string,
+                         std::vector<
+                             std::pair<std::string, std::vector<std::string>>>>
+                         &object : subtree)
+                {
+                    const std::string &path = object.first;
+                    const std::vector<
+                        std::pair<std::string, std::vector<std::string>>>
+                        &connectionNames = object.second;
+
+                    if (!boost::ends_with(path, chassisId))
+                    {
+                        continue;
+                    }
+
+                    if (connectionNames.size() < 1)
+                    {
+                        BMCWEB_LOG_ERROR << "Got 0 Connection names";
+                        continue;
+                    }
+
+                    const std::vector<std::string> &interfaces =
+                        connectionNames[0].second;
+
+                    if (indicatorLed)
+                    {
+                        const std::array<const char *, 2> hasIndicatorLed = {
+                            "xyz.openbmc_project.Inventory.Item.Panel",
+                            "xyz.openbmc_project.Inventory.Item.Board."
+                            "Motherboard"};
+                        bool indicatorChassis = false;
+                        for (const char *interface : hasIndicatorLed)
+                        {
+                            if (std::find(interfaces.begin(), interfaces.end(),
+                                          interface) != interfaces.end())
+                            {
+                                indicatorChassis = true;
+                                break;
+                            }
+                        }
+                        if (indicatorChassis)
+                        {
+                            setIndicatorLedState(asyncResp,
+                                                 std::move(*indicatorLed));
+                        }
+                        else
+                        {
+                            messages::propertyUnknown(asyncResp->res,
+                                                      "IndicatorLED");
+                        }
+                    }
+                    return;
+                }
+
+                messages::resourceNotFound(
+                    asyncResp->res, "#Chassis.v1_10_0.Chassis", chassisId);
+            },
+            "xyz.openbmc_project.ObjectMapper",
+            "/xyz/openbmc_project/object_mapper",
+            "xyz.openbmc_project.ObjectMapper", "GetSubTree",
+            "/xyz/openbmc_project/inventory", 0, interfaces);
+    }
 };
 } // namespace redfish
diff --git a/redfish-core/lib/led.hpp b/redfish-core/lib/led.hpp
new file mode 100644
index 0000000..43e1b5c
--- /dev/null
+++ b/redfish-core/lib/led.hpp
@@ -0,0 +1,157 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#pragma once
+
+#include "async_resp.hpp"
+#include "dbus_utility.hpp"
+#include "redfish_util.hpp"
+
+#include <variant>
+
+namespace redfish
+{
+/**
+ * @brief Retrieves identify led group properties over dbus
+ *
+ * @param[in] aResp     Shared pointer for generating response message.
+ *
+ * @return None.
+ */
+void getIndicatorLedState(std::shared_ptr<AsyncResp> aResp)
+{
+    BMCWEB_LOG_DEBUG << "Get led groups";
+    crow::connections::systemBus->async_method_call(
+        [aResp](const boost::system::error_code ec,
+                const std::variant<bool> asserted) {
+            // Some systems may not have enclosure_identify_blink object so
+            // proceed to get enclosure_identify state.
+            if (!ec)
+            {
+                const bool *blinking = std::get_if<bool>(&asserted);
+                if (!blinking)
+                {
+                    BMCWEB_LOG_DEBUG << "Get identity blinking LED failed";
+                    messages::internalError(aResp->res);
+                    return;
+                }
+                // Blinking ON, no need to check enclosure_identify assert.
+                if (*blinking)
+                {
+                    aResp->res.jsonValue["IndicatorLED"] = "Blinking";
+                    return;
+                }
+            }
+            crow::connections::systemBus->async_method_call(
+                [aResp](const boost::system::error_code ec,
+                        const std::variant<bool> asserted) {
+                    if (!ec)
+                    {
+                        const bool *ledOn = std::get_if<bool>(&asserted);
+                        if (!ledOn)
+                        {
+                            BMCWEB_LOG_DEBUG
+                                << "Get enclosure identity led failed";
+                            messages::internalError(aResp->res);
+                            return;
+                        }
+
+                        if (*ledOn)
+                        {
+                            aResp->res.jsonValue["IndicatorLED"] = "Lit";
+                        }
+                        else
+                        {
+                            aResp->res.jsonValue["IndicatorLED"] = "Off";
+                        }
+                    }
+                    return;
+                },
+                "xyz.openbmc_project.LED.GroupManager",
+                "/xyz/openbmc_project/led/groups/enclosure_identify",
+                "org.freedesktop.DBus.Properties", "Get",
+                "xyz.openbmc_project.Led.Group", "Asserted");
+        },
+        "xyz.openbmc_project.LED.GroupManager",
+        "/xyz/openbmc_project/led/groups/enclosure_identify_blink",
+        "org.freedesktop.DBus.Properties", "Get",
+        "xyz.openbmc_project.Led.Group", "Asserted");
+}
+
+/**
+ * @brief Sets identify led group properties
+ *
+ * @param[in] aResp     Shared pointer for generating response message.
+ * @param[in] ledState  LED state passed from request
+ *
+ * @return None.
+ */
+void setIndicatorLedState(std::shared_ptr<AsyncResp> aResp,
+                          const std::string &ledState)
+{
+    BMCWEB_LOG_DEBUG << "Set led groups";
+    bool ledOn = false;
+    bool ledBlinkng = false;
+
+    if (ledState == "Lit")
+    {
+        ledOn = true;
+    }
+    else if (ledState == "Blinking")
+    {
+        ledBlinkng = true;
+    }
+    else if (ledState != "Off")
+    {
+        messages::propertyValueNotInList(aResp->res, ledState, "IndicatorLED");
+        return;
+    }
+
+    crow::connections::systemBus->async_method_call(
+        [aResp, ledOn, ledBlinkng](const boost::system::error_code ec,
+                                   const std::variant<bool> asserted) mutable {
+            if (ec)
+            {
+                // Some systems may not have enclosure_identify_blink object so
+                // Lets set enclosure_identify state to true if Blinking is
+                // true.
+                if (ledBlinkng)
+                {
+                    ledOn = true;
+                }
+            }
+            crow::connections::systemBus->async_method_call(
+                [aResp](const boost::system::error_code ec,
+                        const std::variant<bool> asserted) {
+                    if (ec)
+                    {
+                        BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
+                        messages::internalError(aResp->res);
+                        return;
+                    }
+                },
+                "xyz.openbmc_project.LED.GroupManager",
+                "/xyz/openbmc_project/led/groups/enclosure_identify",
+                "org.freedesktop.DBus.Properties", "Set",
+                "xyz.openbmc_project.Led.Group", "Asserted",
+                std::variant<bool>(ledOn));
+        },
+        "xyz.openbmc_project.LED.GroupManager",
+        "/xyz/openbmc_project/led/groups/enclosure_identify_blink",
+        "org.freedesktop.DBus.Properties", "Set",
+        "xyz.openbmc_project.Led.Group", "Asserted",
+        std::variant<bool>(ledBlinkng));
+}
+} // namespace redfish
\ No newline at end of file
diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp
index e9ccde1..9280d8f 100644
--- a/redfish-core/lib/systems.hpp
+++ b/redfish-core/lib/systems.hpp
@@ -16,6 +16,7 @@
 #pragma once
 
 #include "health.hpp"
+#include "led.hpp"
 #include "pcie.hpp"
 #include "redfish_util.hpp"
 
@@ -587,137 +588,6 @@
 }
 
 /**
- * @brief Retrieves identify led group properties over dbus
- *
- * @param[in] aResp     Shared pointer for generating response message.
- *
- * @return None.
- */
-void getIndicatorLedState(std::shared_ptr<AsyncResp> aResp)
-{
-    BMCWEB_LOG_DEBUG << "Get led groups";
-    crow::connections::systemBus->async_method_call(
-        [aResp](const boost::system::error_code ec,
-                const std::variant<bool> asserted) {
-            // Some systems may not have enclosure_identify_blink object so
-            // proceed to get enclosure_identify state.
-            if (!ec)
-            {
-                const bool *blinking = std::get_if<bool>(&asserted);
-                if (!blinking)
-                {
-                    BMCWEB_LOG_DEBUG << "Get identity blinking LED failed";
-                    messages::internalError(aResp->res);
-                    return;
-                }
-                // Blinking ON, no need to check enclosure_identify assert.
-                if (*blinking)
-                {
-                    aResp->res.jsonValue["IndicatorLED"] = "Blinking";
-                    return;
-                }
-            }
-            crow::connections::systemBus->async_method_call(
-                [aResp](const boost::system::error_code ec,
-                        const std::variant<bool> asserted) {
-                    if (!ec)
-                    {
-                        const bool *ledOn = std::get_if<bool>(&asserted);
-                        if (!ledOn)
-                        {
-                            BMCWEB_LOG_DEBUG
-                                << "Get enclosure identity led failed";
-                            messages::internalError(aResp->res);
-                            return;
-                        }
-
-                        if (*ledOn)
-                        {
-                            aResp->res.jsonValue["IndicatorLED"] = "Lit";
-                        }
-                        else
-                        {
-                            aResp->res.jsonValue["IndicatorLED"] = "Off";
-                        }
-                    }
-                    return;
-                },
-                "xyz.openbmc_project.LED.GroupManager",
-                "/xyz/openbmc_project/led/groups/enclosure_identify",
-                "org.freedesktop.DBus.Properties", "Get",
-                "xyz.openbmc_project.Led.Group", "Asserted");
-        },
-        "xyz.openbmc_project.LED.GroupManager",
-        "/xyz/openbmc_project/led/groups/enclosure_identify_blink",
-        "org.freedesktop.DBus.Properties", "Get",
-        "xyz.openbmc_project.Led.Group", "Asserted");
-}
-/**
- * @brief Sets identify led group properties
- *
- * @param[in] aResp     Shared pointer for generating response message.
- * @param[in] ledState  LED state passed from request
- *
- * @return None.
- */
-void setIndicatorLedState(std::shared_ptr<AsyncResp> aResp,
-                          const std::string &ledState)
-{
-    BMCWEB_LOG_DEBUG << "Set led groups";
-    bool ledOn = false;
-    bool ledBlinkng = false;
-
-    if (ledState == "Lit")
-    {
-        ledOn = true;
-    }
-    else if (ledState == "Blinking")
-    {
-        ledBlinkng = true;
-    }
-    else if (ledState != "Off")
-    {
-        messages::propertyValueNotInList(aResp->res, ledState, "IndicatorLED");
-        return;
-    }
-
-    crow::connections::systemBus->async_method_call(
-        [aResp, ledOn, ledBlinkng](const boost::system::error_code ec,
-                                   const std::variant<bool> asserted) mutable {
-            if (ec)
-            {
-                // Some systems may not have enclosure_identify_blink object so
-                // Lets set enclosure_identify state to true if Blinking is
-                // true.
-                if (ledBlinkng)
-                {
-                    ledOn = true;
-                }
-            }
-            crow::connections::systemBus->async_method_call(
-                [aResp](const boost::system::error_code ec,
-                        const std::variant<bool> asserted) {
-                    if (ec)
-                    {
-                        BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
-                        messages::internalError(aResp->res);
-                        return;
-                    }
-                },
-                "xyz.openbmc_project.LED.GroupManager",
-                "/xyz/openbmc_project/led/groups/enclosure_identify",
-                "org.freedesktop.DBus.Properties", "Set",
-                "xyz.openbmc_project.Led.Group", "Asserted",
-                std::variant<bool>(ledOn));
-        },
-        "xyz.openbmc_project.LED.GroupManager",
-        "/xyz/openbmc_project/led/groups/enclosure_identify_blink",
-        "org.freedesktop.DBus.Properties", "Set",
-        "xyz.openbmc_project.Led.Group", "Asserted",
-        std::variant<bool>(ledBlinkng));
-}
-
-/**
  * @brief Retrieves host state properties over dbus
  *
  * @param[in] aResp     Shared pointer for completing asynchronous calls.