Dynamically determine ChassisType for Redfish

The function handleChassisGetSubTree will now handle properties that
are part of the xyz.openbmc_project.Inventory.Item.Chassis interface.
At time of development this is only ChassisType. The new function
"handleChassisProperties" will attempt to get the Type property from
the interface, translate it to a Redfish standard string and set it as
the ChassisType value. If the property cannot be found the default
"RackMount" will be used.

Tested: Added and ran 4 new unit tests. Ran manual tests with
ChassisType being exposed via dbus in QEMU emulated environments.
Tested on ASPEED 2600 eval board.

Change-Id: Ibbd048db5007f5154e88495ec6e651a3a2137b06
Signed-off-by: Joseph-Jonathan Salzano <joseph-jonathan.salzano@hp.com>
diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp
index d5ca046..51effb6 100644
--- a/redfish-core/lib/chassis.hpp
+++ b/redfish-core/lib/chassis.hpp
@@ -35,12 +35,59 @@
 #include <sdbusplus/unpack_properties.hpp>
 
 #include <array>
+#include <memory>
 #include <ranges>
 #include <string_view>
 
 namespace redfish
 {
 
+inline chassis::ChassisType
+    translateChassisTypeToRedfish(const std::string_view& chassisType)
+{
+    if (chassisType ==
+        "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Blade")
+    {
+        return chassis::ChassisType::Blade;
+    }
+    if (chassisType ==
+        "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Component")
+    {
+        return chassis::ChassisType::Component;
+    }
+    if (chassisType ==
+        "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Enclosure")
+    {
+        return chassis::ChassisType::Enclosure;
+    }
+    if (chassisType ==
+        "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Module")
+    {
+        return chassis::ChassisType::Module;
+    }
+    if (chassisType ==
+        "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.RackMount")
+    {
+        return chassis::ChassisType::RackMount;
+    }
+    if (chassisType ==
+        "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.StandAlone")
+    {
+        return chassis::ChassisType::StandAlone;
+    }
+    if (chassisType ==
+        "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.StorageEnclosure")
+    {
+        return chassis::ChassisType::StorageEnclosure;
+    }
+    if (chassisType ==
+        "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Zone")
+    {
+        return chassis::ChassisType::Zone;
+    }
+    return chassis::ChassisType::Invalid;
+}
+
 /**
  * @brief Retrieves resources over dbus to link to the chassis
  *
@@ -463,6 +510,36 @@
     getStorageLink(asyncResp, path);
 }
 
+inline void handleChassisProperties(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const dbus::utility::DBusPropertiesMap& propertiesList)
+{
+    const std::string* type = nullptr;
+
+    const bool success = sdbusplus::unpackPropertiesNoThrow(
+        dbus_utils::UnpackErrorPrinter(), propertiesList, "Type", type);
+
+    if (!success)
+    {
+        messages::internalError(asyncResp->res);
+        return;
+    }
+
+    if (type != nullptr)
+    {
+        auto chassisType = translateChassisTypeToRedfish(*type);
+        if (chassisType != chassis::ChassisType::Invalid)
+        {
+            asyncResp->res.jsonValue["ChassisType"] = chassisType;
+        }
+    }
+    else
+    {
+        asyncResp->res.jsonValue["ChassisType"] =
+            chassis::ChassisType::RackMount;
+    }
+}
+
 inline void handleChassisGetSubTree(
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
     const std::string& chassisId, const boost::system::error_code& ec,
@@ -502,8 +579,6 @@
         asyncResp->res.jsonValue["@odata.id"] =
             boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
         asyncResp->res.jsonValue["Name"] = "Chassis Collection";
-        asyncResp->res.jsonValue["ChassisType"] =
-            chassis::ChassisType::RackMount;
         asyncResp->res.jsonValue["Actions"]["#Chassis.Reset"]["target"] =
             boost::urls::format("/redfish/v1/Chassis/{}/Actions/Chassis.Reset",
                                 chassisId);
@@ -616,6 +691,15 @@
                                                propertiesList);
             });
 
+        sdbusplus::asio::getAllProperties(
+            *crow::connections::systemBus, connectionName, path,
+            "xyz.openbmc_project.Inventory.Item.Chassis",
+            [asyncResp](
+                const boost::system::error_code&,
+                const dbus::utility::DBusPropertiesMap& propertiesList) {
+                handleChassisProperties(asyncResp, propertiesList);
+            });
+
         for (const auto& interface : interfaces2)
         {
             if (interface == "xyz.openbmc_project.Common.UUID")
diff --git a/test/redfish-core/lib/chassis_test.cpp b/test/redfish-core/lib/chassis_test.cpp
index 957b5eb..c6186cf 100644
--- a/test/redfish-core/lib/chassis_test.cpp
+++ b/test/redfish-core/lib/chassis_test.cpp
@@ -1,10 +1,13 @@
 #include "app.hpp"
 #include "async_resp.hpp"
 #include "chassis.hpp"
+#include "dbus_utility.hpp"
+#include "generated/enums/chassis.hpp"
 #include "http_request.hpp"
 #include "http_response.hpp"
 
 #include <boost/beast/core/string_type.hpp>
+#include <boost/beast/http/status.hpp>
 #include <boost/beast/http/verb.hpp>
 #include <nlohmann/json.hpp>
 
@@ -59,5 +62,97 @@
     handleChassisResetActionInfoGet(app, request, response, fakeChassis);
 }
 
+TEST(TranslateChassisTypeToRedfish, TranslationsAreExpected)
+{
+    ASSERT_EQ(
+        chassis::ChassisType::Blade,
+        translateChassisTypeToRedfish(
+            "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Blade"));
+    ASSERT_EQ(
+        chassis::ChassisType::Component,
+        translateChassisTypeToRedfish(
+            "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Component"));
+    ASSERT_EQ(
+        chassis::ChassisType::Enclosure,
+        translateChassisTypeToRedfish(
+            "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Enclosure"));
+    ASSERT_EQ(
+        chassis::ChassisType::Module,
+        translateChassisTypeToRedfish(
+            "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Module"));
+    ASSERT_EQ(
+        chassis::ChassisType::RackMount,
+        translateChassisTypeToRedfish(
+            "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.RackMount"));
+    ASSERT_EQ(
+        chassis::ChassisType::StandAlone,
+        translateChassisTypeToRedfish(
+            "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.StandAlone"));
+    ASSERT_EQ(
+        chassis::ChassisType::StorageEnclosure,
+        translateChassisTypeToRedfish(
+            "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.StorageEnclosure"));
+    ASSERT_EQ(
+        chassis::ChassisType::Zone,
+        translateChassisTypeToRedfish(
+            "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Zone"));
+    ASSERT_EQ(
+        chassis::ChassisType::Invalid,
+        translateChassisTypeToRedfish(
+            "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Unknown"));
+}
+
+TEST(HandleChassisProperties, TypeFound)
+{
+    auto response = std::make_shared<bmcweb::AsyncResp>();
+    auto properties = dbus::utility::DBusPropertiesMap();
+    properties.emplace_back(
+        std::string("Type"),
+        dbus::utility::DbusVariantType(
+            "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.RackMount"));
+    handleChassisProperties(response, properties);
+    ASSERT_EQ("RackMount", response->res.jsonValue["ChassisType"]);
+
+    response = std::make_shared<bmcweb::AsyncResp>();
+    properties.clear();
+    properties.emplace_back(
+        std::string("Type"),
+        dbus::utility::DbusVariantType(
+            "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.StandAlone"));
+    handleChassisProperties(response, properties);
+    ASSERT_EQ("StandAlone", response->res.jsonValue["ChassisType"]);
+}
+
+TEST(HandleChassisProperties, BadTypeFound)
+{
+    auto response = std::make_shared<bmcweb::AsyncResp>();
+    auto properties = dbus::utility::DBusPropertiesMap();
+    properties.emplace_back(
+        std::string("Type"),
+        dbus::utility::DbusVariantType(
+            "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Unknown"));
+    handleChassisProperties(response, properties);
+    ASSERT_FALSE(response->res.jsonValue.contains("ChassisType"));
+}
+
+TEST(HandleChassisProperties, FailToGetProperty)
+{
+    auto response = std::make_shared<bmcweb::AsyncResp>();
+    auto properties = dbus::utility::DBusPropertiesMap();
+    properties.emplace_back(std::string("Type"),
+                            dbus::utility::DbusVariantType(123));
+    handleChassisProperties(response, properties);
+    ASSERT_EQ(boost::beast::http::status::internal_server_error,
+              response->res.result());
+}
+
+TEST(HandleChassisProperties, TypeNotFound)
+{
+    auto response = std::make_shared<bmcweb::AsyncResp>();
+    auto properties = dbus::utility::DBusPropertiesMap();
+    handleChassisProperties(response, properties);
+    ASSERT_EQ("RackMount", response->res.jsonValue["ChassisType"]);
+}
+
 } // namespace
 } // namespace redfish