fault-monitor: Add OperationalStatus Functional monitoring

Currently, the default way is to update the Asserted property of
the LedManager by monitoring the `/xyz/openbmc_project/logging`
object path.

The intent behind this commit is to add another way to monitor the
`xyz.openbmc_project.State.Decorator.OperationalStatus` interface
of the Inventory D-Bus object, and check whether the Inventory
D-Bus object is associated with the LED group D-Bus object, and
then update the Asserted property of the LedManager.

Since both these methods handle the faults differently,
Only ONE of these 2 methods can be enabled and NOT both.

The first way is supported by default. To turn OFF the default way
AND turn ON this second way, Enable monitor-operational-status.

Tested:
- enable `monitor-operational-status` in bbappend file, built successfully.

- Before changing the `OperationalStatus` interface:
  busctl get-property xyz.openbmc_project.Inventory.Manager
  /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply1
  xyz.openbmc_project.State.Decorator.OperationalStatus Functional
  b true

  busctl get-property xyz.openbmc_project.LED.GroupManager
  /xyz/openbmc_project/led/groups/powersupply1_fault
  xyz.openbmc_project.Led.Group Asserted
  b false

  busctl get-property xyz.openbmc_project.LED.Controller.front_psu
  /xyz/openbmc_project/led/physical/front_psu xyz.openbmc_project.Led.Physical State
  s "xyz.openbmc_project.Led.Physical.Action.Off"

- After changing the `OperationalStatus` interface:
  busctl set-property xyz.openbmc_project.Inventory.
  /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply1
  xyz.openbmc_project.State.Decorator.OperationalStatus Functional b false

  busctl get-property xyz.openbmc_project.Inventory.Manager
  /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply1
  xyz.openbmc_project.State.Decorator.OperationalStatus Functional
  b false

  (
	If we have more than 1 LED Group D-Bus object:
	Like: The system/chassis/motherboard/powersupply1 path contains
	      two sets of LEDGroup
	{
        "types":
        {
            "rType": "fru",
            "fType": "fault_led_group"
        },
        "paths":
        [
            "/xyz/openbmc_project/led/groups/powersupply0_fault",
            "/xyz/openbmc_project/led/groups/powersupply1_fault"
        ]
    }
  busctl get-property xyz.openbmc_project.LED.GroupManager
  /xyz/openbmc_project/led/groups/powersupply0_fault xyz.openbmc_project.Led.Group Asserted
  b true
  )

  busctl get-property xyz.openbmc_project.LED.GroupManager
  /xyz/openbmc_project/led/groups/powersupply1_fault xyz.openbmc_project.Led.Group Asserted
  b true

  busctl get-property xyz.openbmc_project.LED.Controller.front_psu
  /xyz/openbmc_project/led/physical/front_psu xyz.openbmc_project.Led.Physical State
  s "xyz.openbmc_project.Led.Physical.Action.Blink"

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I36c47198168028ea2b55804cf66d0285847afbd4
diff --git a/fault-monitor/operational-status-monitor.cpp b/fault-monitor/operational-status-monitor.cpp
new file mode 100644
index 0000000..7a6595f
--- /dev/null
+++ b/fault-monitor/operational-status-monitor.cpp
@@ -0,0 +1,113 @@
+#include "operational-status-monitor.hpp"
+
+#include <phosphor-logging/elog.hpp>
+
+namespace phosphor
+{
+namespace led
+{
+namespace Operational
+{
+namespace status
+{
+namespace monitor
+{
+using namespace phosphor::logging;
+
+void Monitor::matchHandler(sdbusplus::message::message& msg)
+{
+    // Get the ObjectPath of the `xyz.openbmc_project.Inventory.Manager`
+    // service
+    std::string invObjectPath = msg.get_path();
+
+    // Get all the properties of
+    // "xyz.openbmc_project.State.Decorator.OperationalStatus" interface
+    std::string interfaceName{};
+    std::map<std::string, std::variant<bool>> properties;
+    msg.read(interfaceName, properties);
+
+    const auto it = properties.find("Functional");
+    if (it != properties.end())
+    {
+        const bool* value = std::get_if<bool>(&it->second);
+        if (!value)
+        {
+            log<level::ERR>("Faild to get the Functional property",
+                            entry("INVENTORY_PATH=%s", invObjectPath.c_str()));
+            return;
+        }
+
+        // See if the Inventory D-Bus object has an association with LED groups
+        // D-Bus object.
+        auto ledGroupPath = getLedGroupPaths(invObjectPath);
+        if (ledGroupPath.empty())
+        {
+            log<level::INFO>("The inventory D-Bus object is not associated "
+                             "with the LED group D-Bus object.",
+                             entry("INVENTORY_PATH=%s", invObjectPath.c_str()));
+            return;
+        }
+
+        // Update the Asserted property by the Functional property value.
+        updateAssertedProperty(ledGroupPath, *value);
+    }
+}
+
+const std::vector<std::string>
+    Monitor::getLedGroupPaths(const std::string& inventoryPath) const
+{
+    // Get endpoints from the rType
+    std::string faultLedAssociation = inventoryPath + "/fault_led_group";
+
+    // endpoint contains the vector of strings, where each string is a Inventory
+    // D-Bus object that this, associated with this LED Group D-Bus object
+    // pointed to by fru_fault
+    PropertyValue endpoint{};
+
+    try
+    {
+        endpoint = dBusHandler.getProperty(faultLedAssociation,
+                                           "xyz.openbmc_project.Association",
+                                           "endpoints");
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        log<level::ERR>("Failed to get endpoints property",
+                        entry("ERROR=%s", e.what()),
+                        entry("PATH=%s", faultLedAssociation.c_str()));
+
+        return {};
+    }
+
+    auto& endpoints = std::get<std::vector<std::string>>(endpoint);
+
+    return endpoints;
+}
+
+void Monitor::updateAssertedProperty(
+    const std::vector<std::string>& ledGroupPaths, bool value)
+{
+    for (const auto& path : ledGroupPaths)
+    {
+        try
+        {
+            // Call "Group Asserted --> true" if the value of Functional is
+            // false Call "Group Asserted --> false" if the value of Functional
+            // is true
+            PropertyValue assertedValue{!value};
+            dBusHandler.setProperty(path, "xyz.openbmc_project.Led.Group",
+                                    "Asserted", assertedValue);
+        }
+        catch (const sdbusplus::exception::SdBusError& e)
+        {
+            log<level::ERR>("Failed to set Asserted property",
+                            entry("ERROR=%s", e.what()),
+                            entry("PATH=%s", path.c_str()));
+        }
+    }
+}
+} // namespace monitor
+} // namespace status
+} // namespace Operational
+} // namespace led
+} // namespace phosphor