Update status in Redfish

Create associations when failed to change the
status to Warning.

Tested:

{
    "@odata.context": "/redfish/v1/$metadata#Drive.Drive",
    "@odata.id": "/redfish/v1/Systems/system/Storage/1/Drive/Drive_1",
    "@odata.type": "#Drive.v1_7_0.Drive",
    "Status": {
        "Health": "Warning",
        "HealthRollup": "Warning",
        "State": "Enabled"
    }
}

Change-Id: I3e5f87dc1253e8f24396a924e79d5474b0e5754a
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/hsbp-manager/include/utils.hpp b/hsbp-manager/include/utils.hpp
index 0b47a54..a3b0294 100644
--- a/hsbp-manager/include/utils.hpp
+++ b/hsbp-manager/include/utils.hpp
@@ -71,6 +71,12 @@
 const static constexpr char* property = "CurrentHostState";
 } // namespace power
 
+namespace association
+{
+const static constexpr char* interface =
+    "xyz.openbmc_project.Association.Definitions";
+} // namespace association
+
 namespace hsbp
 {
 enum class registers : uint8_t
diff --git a/hsbp-manager/src/hsbp_manager.cpp b/hsbp-manager/src/hsbp_manager.cpp
index f25f266..d65d80c 100644
--- a/hsbp-manager/src/hsbp_manager.cpp
+++ b/hsbp-manager/src/hsbp_manager.cpp
@@ -145,9 +145,10 @@
 
 struct Drive
 {
-    Drive(size_t driveIndex, bool isPresent, bool isOperational, bool nvme,
+    Drive(size_t driveIndex, bool present, bool isOperational, bool nvme,
           bool rebuilding) :
-        isNvme(nvme)
+        isNvme(nvme),
+        isPresent(present)
     {
         constexpr const char* basePath =
             "/xyz/openbmc_project/inventory/item/drive/Drive_";
@@ -160,7 +161,28 @@
         operationalIface = objServer.add_interface(
             itemIface->get_object_path(),
             "xyz.openbmc_project.State.Decorator.OperationalStatus");
-        operationalIface->register_property("Functional", isOperational);
+
+        operationalIface->register_property(
+            "Functional", isOperational,
+            [this](const bool req, bool& property) {
+                if (!isPresent)
+                {
+                    return 0;
+                }
+                if (property == req)
+                {
+                    return 1;
+                }
+                property = req;
+                if (req)
+                {
+                    clearFailed();
+                    return 1;
+                }
+                markFailed();
+                return 1;
+            });
+
         operationalIface->initialize();
         rebuildingIface = objServer.add_interface(
             itemIface->get_object_path(), "xyz.openbmc_project.State.Drive");
@@ -170,6 +192,16 @@
             objServer.add_interface(itemIface->get_object_path(),
                                     "xyz.openbmc_project.Inventory.Item.Drive");
         driveIface->initialize();
+        associations = objServer.add_interface(itemIface->get_object_path(),
+                                               association::interface);
+        associations->register_property("Associations",
+                                        std::vector<Association>{});
+        associations->initialize();
+
+        if (isPresent && (!isOperational || rebuilding))
+        {
+            markFailed();
+        }
     }
     virtual ~Drive()
     {
@@ -178,6 +210,7 @@
         objServer.remove_interface(rebuildingIface);
         objServer.remove_interface(assetIface);
         objServer.remove_interface(driveIface);
+        objServer.remove_interface(associations);
     }
 
     void createAsset(
@@ -197,13 +230,38 @@
         assetIface->initialize();
     }
 
+    void markFailed(void)
+    {
+        // todo: maybe look this up via mapper
+        constexpr const char* globalInventoryPath =
+            "/xyz/openbmc_project/CallbackManager";
+
+        if (!isPresent)
+        {
+            return;
+        }
+
+        operationalIface->set_property("Functional", false);
+        std::vector<Association> warning = {
+            {"", "warning", globalInventoryPath}};
+        associations->set_property("Associations", warning);
+    }
+
+    void clearFailed(void)
+    {
+        operationalIface->set_property("Functional", true);
+        associations->set_property("Associations", std::vector<Association>{});
+    }
+
     std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
     std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
     std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface;
     std::shared_ptr<sdbusplus::asio::dbus_interface> assetIface;
     std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> associations;
 
     bool isNvme;
+    bool isPresent;
 };
 
 struct Backplane
@@ -337,18 +395,27 @@
     {
 
         uint8_t nvme = ifdet ^ presence;
-        for (size_t ii = 0; ii < maxDrives; ii++)
+        size_t ii = 0;
+
+        for (auto it = drives.begin(); it != drives.end(); it++, ii++)
         {
             bool isNvme = nvme & (1 << ii);
             bool isPresent = isNvme || (presence & (1 << ii));
             bool isFailed = !isPresent || (failed & (1 << ii));
             bool isRebuilding = isPresent && (rebuilding & (1 << ii));
 
-            Drive& drive = drives[ii];
-            drive.isNvme = isNvme;
-            drive.itemIface->set_property("Present", isPresent);
-            drive.operationalIface->set_property("Functional", !isFailed);
-            drive.rebuildingIface->set_property("Rebuilding", isRebuilding);
+            it->isNvme = isNvme;
+            it->itemIface->set_property("Present", isPresent);
+            it->isPresent = isPresent;
+            it->rebuildingIface->set_property("Rebuilding", isRebuilding);
+            if (isFailed || isRebuilding)
+            {
+                it->markFailed();
+            }
+            else
+            {
+                it->clearFailed();
+            }
         }
     }
 
@@ -508,13 +575,13 @@
     std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
     std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
 
-    std::vector<Drive> drives;
+    std::list<Drive> drives;
     std::vector<std::shared_ptr<Led>> leds;
     std::shared_ptr<boost::container::flat_set<Mux>> muxes;
 };
 
 std::unordered_map<std::string, Backplane> backplanes;
-std::vector<Drive> ownerlessDrives; // drives without a backplane
+std::list<Drive> ownerlessDrives; // drives without a backplane
 
 static size_t getDriveCount()
 {
@@ -695,9 +762,10 @@
                                       << " " << driveIndex << "\n";
                             return;
                         }
+                        auto it = parent->drives.begin();
+                        std::advance(it, driveIndex);
 
-                        Drive& drive = parent->drives[driveIndex];
-                        drive.createAsset(assetInventory);
+                        it->createAsset(assetInventory);
                     },
                     owner, path, "org.freedesktop.DBus.Properties", "GetAll",
                     "" /*all interface items*/);
@@ -925,6 +993,10 @@
             });
         });
 
+    auto iface =
+        objServer.add_interface("/xyz/openbmc_project/inventory/item/storage",
+                                "xyz.openbmc_project.inventory.item.storage");
+
     io.post([]() { populate(); });
     setupPowerMatch(conn);
     io.run();