Add association between chassis and drive

This commit adds an association between the storage device ("drive") and
the associated chassis. Specifically, the new association is the
following:
  ["chassis", "drive", <chassis_path>]

This association will be used in bmcweb to add Redfish links between
drive and chassis.

Tested:
$ busctl get-property xyz.openbmc_project.eStoraged \
  /xyz/openbmc_project/inventory/storage/mmcblk0 \
  xyz.openbmc_project.Association.Definitions Associations
$ busctl get-property xyz.openbmc_project.ObjectMapper \
  /xyz/openbmc_project/inventory/system/board/dcscm/drive \
  xyz.openbmc_project.Association endpoints
$ busctl get-property xyz.openbmc_project.ObjectMapper \
  /xyz/openbmc_project/inventory/storage/mmcblk0/chassis \
  xyz.openbmc_project.Association endpoints

Signed-off-by: John Wedig <johnwedig@google.com>
Change-Id: Ie21725e9ceb5134ac94854dcb06f3b86a48eeabd
diff --git a/src/estoraged.cpp b/src/estoraged.cpp
index 6ea2cb2..af20d32 100644
--- a/src/estoraged.cpp
+++ b/src/estoraged.cpp
@@ -26,13 +26,15 @@
 namespace estoraged
 {
 
+using Association = std::tuple<std::string, std::string, std::string>;
 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
 using sdbusplus::xyz::openbmc_project::Common::Error::UnsupportedRequest;
 using sdbusplus::xyz::openbmc_project::Inventory::Item::server::Volume;
 
 EStoraged::EStoraged(sdbusplus::asio::object_server& server,
-                     const std::string& devPath, const std::string& luksName,
-                     uint64_t size, uint8_t lifeTime,
+                     const std::string& configPath, const std::string& devPath,
+                     const std::string& luksName, uint64_t size,
+                     uint8_t lifeTime,
                      std::unique_ptr<CryptsetupInterface> cryptInterface,
                      std::unique_ptr<FilesystemInterface> fsInterface) :
     devPath(devPath),
@@ -43,11 +45,12 @@
     /* Get the filename of the device (without "/dev/"). */
     std::string deviceName = std::filesystem::path(devPath).filename().string();
     /* DBus object path */
-    std::string path = "/xyz/openbmc_project/inventory/storage/" + deviceName;
+    std::string objectPath =
+        "/xyz/openbmc_project/inventory/storage/" + deviceName;
 
     /* Add Volume interface. */
     volumeInterface = objectServer.add_interface(
-        path, "xyz.openbmc_project.Inventory.Item.Volume");
+        objectPath, "xyz.openbmc_project.Inventory.Item.Volume");
     volumeInterface->register_method(
         "FormatLuks", [this](const std::vector<uint8_t>& password,
                              Volume::FilesystemType type) {
@@ -74,19 +77,30 @@
 
     /* Add Drive interface. */
     driveInterface = objectServer.add_interface(
-        path, "xyz.openbmc_project.Inventory.Item.Drive");
+        objectPath, "xyz.openbmc_project.Inventory.Item.Drive");
     driveInterface->register_property("Capacity", size);
     driveInterface->register_property("PredictedMediaLifeLeftPercent",
                                       lifeTime);
 
     volumeInterface->initialize();
     driveInterface->initialize();
+
+    /* Set up the association between chassis and drive. */
+    association = objectServer.add_interface(
+        objectPath, "xyz.openbmc_project.Association.Definitions");
+
+    std::vector<Association> associations;
+    associations.emplace_back("chassis", "drive",
+                              std::filesystem::path(configPath).parent_path());
+    association->register_property("Associations", associations);
+    association->initialize();
 }
 
 EStoraged::~EStoraged()
 {
     objectServer.remove_interface(volumeInterface);
     objectServer.remove_interface(driveInterface);
+    objectServer.remove_interface(association);
 }
 
 void EStoraged::formatLuks(const std::vector<uint8_t>& password,
diff --git a/src/main.cpp b/src/main.cpp
index 5476dbb..a32dbfe 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -96,7 +96,7 @@
                         sysfsDir);
                 /* Create the storage object. */
                 storageObjects[path] = std::make_unique<estoraged::EStoraged>(
-                    objectServer, deviceFile, luksName, size, lifeleft);
+                    objectServer, path, deviceFile, luksName, size, lifeleft);
                 lg2::info("Created eStoraged object for path {PATH}", "PATH",
                           path, "REDFISH_MESSAGE_ID",
                           std::string("OpenBMC.0.1.CreateStorageObjects"));
diff --git a/src/test/estoraged_test.cpp b/src/test/estoraged_test.cpp
index b98144e..89581ab 100644
--- a/src/test/estoraged_test.cpp
+++ b/src/test/estoraged_test.cpp
@@ -38,13 +38,11 @@
   public:
     const char* testFileName = "testfile";
     const char* testLuksDevName = "testfile_luksDev";
+    const std::string testConfigPath =
+        "/xyz/openbmc_project/inventory/system/board/test_board/test_emmc";
     const uint64_t testSize = 24;
     const uint8_t testLifeTime = 25;
     std::ofstream testFile;
-    const char* testPath = "/test/openbmc_project/storage/test_dev";
-    const char* estoragedInterface =
-        "xyz.openbmc_project.Inventory.Item.Volume";
-    const char* driveInterface = "xyz.openbmc_project.Inventory.Item.Drive";
     std::string passwordString;
     std::vector<uint8_t> password;
     MockCryptsetupInterface* mockCryptIface{};
@@ -83,8 +81,8 @@
         objectServer = std::make_unique<sdbusplus::asio::object_server>(conn);
 
         esObject = std::make_unique<estoraged::EStoraged>(
-            *objectServer, testFileName, testLuksDevName, testSize,
-            testLifeTime, std::move(cryptIface), std::move(fsIface));
+            *objectServer, testConfigPath, testFileName, testLuksDevName,
+            testSize, testLifeTime, std::move(cryptIface), std::move(fsIface));
     }
 
     void TearDown() override