| #include "chassis.hpp" | |
| #include <memory> | |
| #include <string> | |
| #include <system_error> // NOLINT | |
| #include <unordered_set> | |
| #include <utility> | |
| #include <vector> | |
| #include <gmock/gmock.h> | |
| #include <gtest/gtest.h> | |
| #include "absl/strings/str_cat.h" | |
| #include "async_resp.hpp" | |
| #include "dbus_utility.hpp" // IWYU pragma: keep | |
| #include "subprocess_utils.hpp" | |
| #include "subprocess_utils_fake.hpp" | |
| #include "test/redfish-core/lib/snapshot_fixture.hpp" | |
| #include <nlohmann/json.hpp> | |
| #include "managed_store.hpp" | |
| #include "managed_store_types.hpp" | |
| #include "test/g3/mock_managed_store.hpp" | |
| #include "test/g3/mock_managed_store_test.hpp" | |
| #include "sdbusplus/message/native_types.hpp" | |
| namespace redfish { | |
| namespace { | |
| using ::managedStore::KeyType; | |
| using ::managedStore::ManagedType; | |
| using ::testing::Contains; | |
| using ::testing::NotNull; | |
| constexpr char fakeChassis[] = "fakeChassis"; | |
| TEST(HandleChassisResetActionInfoGet, StaticAttributesAreExpected) { | |
| auto response = std::make_shared<bmcweb::AsyncResp>(); | |
| std::error_code err; | |
| validateChassisResetActionInfoHandler(response, fakeChassis, err, | |
| "RackMount"); | |
| nlohmann::json& json = response->res.jsonValue; | |
| EXPECT_EQ(json["@odata.type"], "#ActionInfo.v1_1_2.ActionInfo"); | |
| EXPECT_EQ(json["@odata.id"], absl::StrCat("/redfish/v1/Chassis/", fakeChassis, | |
| "/ResetActionInfo")); | |
| EXPECT_EQ(json["Name"], "Reset Action Info"); | |
| EXPECT_EQ(json["Id"], "ResetActionInfo"); | |
| nlohmann::json::array_t parameters; | |
| nlohmann::json::object_t delayParameter; | |
| delayParameter["Name"] = "Delay"; | |
| delayParameter["Required"] = false; | |
| delayParameter["DataType"] = "Number"; | |
| nlohmann::json::object_t parameter; | |
| parameter["Name"] = "ResetType"; | |
| parameter["Required"] = true; | |
| parameter["DataType"] = "String"; | |
| nlohmann::json::array_t allowed; | |
| allowed.push_back("PowerCycle"); | |
| parameter["AllowableValues"] = std::move(allowed); | |
| parameters.push_back(std::move(parameter)); | |
| parameters.push_back(std::move(delayParameter)); | |
| EXPECT_EQ(json["Parameters"], parameters); | |
| } | |
| TEST_F(SnapshotFixture, ChassisTypeTestReturnsCorrectResponse) { | |
| auto response = std::make_shared<bmcweb::AsyncResp>(); | |
| nlohmann::json& json = response->res.jsonValue; | |
| json["ChassisType"] = "RackMount"; | |
| addChassisReset(response, fakeChassis, "UnsupportedType"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(json["ChassisType"], "UnsupportedType"); | |
| } | |
| TEST_F(SnapshotFixture, GetChassisCollectionTestReturnsCorrectResposne) { | |
| handleChassisCollectionGet(app_, CreateRequest(), share_async_resp_); | |
| RunIoUntilDone(); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["@odata.id"], "/redfish/v1/Chassis"); | |
| EXPECT_EQ(json["@odata.type"], "#ChassisCollection.ChassisCollection"); | |
| EXPECT_EQ(json["Name"], "Chassis Collection"); | |
| EXPECT_EQ(json["Members@odata.count"], json["Members"].size()); | |
| std::unordered_set<std::string> chassis_set{ | |
| {"/redfish/v1/Chassis/Bigchassis2_1", "/redfish/v1/Chassis/chassis1V2ND", | |
| "/redfish/v1/Chassis/chassis3_1", "/redfish/v1/Chassis/chassis4_1", | |
| "/redfish/v1/Chassis/coin_battery", "/redfish/v1/Chassis/i2cool_0", | |
| "/redfish/v1/Chassis/i2cool_1", "/redfish/v1/Chassis/i2cool_2", | |
| "/redfish/v1/Chassis/ioadapter_x16_1", "/redfish/v1/Chassis/platform1"}}; | |
| EXPECT_EQ(json["Members"].size(), chassis_set.size()); | |
| for (const auto& member : json["Members"]) { | |
| ASSERT_TRUE(member.contains("@odata.id")); | |
| const std::string* chassis = | |
| member["@odata.id"].get_ptr<const std::string*>(); | |
| ASSERT_THAT(chassis, NotNull()); | |
| EXPECT_THAT(chassis_set, Contains(*chassis)); | |
| } | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| } | |
| void CheckChassisProcessorsAndSubsystems(nlohmann::json& json, | |
| const std::string& systemPath, | |
| const bool expectPcieMemory) { | |
| EXPECT_EQ(json["Links"]["Processors"][0]["@odata.id"], | |
| systemPath + "/Processors/cpu0"); | |
| EXPECT_EQ(json["Links"]["Processors"][1]["@odata.id"], | |
| systemPath + "/Processors/cpu1"); | |
| if (expectPcieMemory) { | |
| EXPECT_EQ(json["Memory"]["@odata.id"], systemPath + "/Memory"); | |
| EXPECT_EQ(json["PCIeDevices"]["@odata.id"], systemPath + "/PCIeDevices"); | |
| } else { | |
| EXPECT_TRUE(json["Memory"].is_null()); | |
| EXPECT_TRUE(json["PCIeDevices"].is_null()); | |
| } | |
| } | |
| TEST_F(SnapshotFixture, GetChassisTestReturnsCorrectResposne) { | |
| #ifdef BMCWEB_ALLOW_NON_HARDCODED_SINGLEHOST_SYSTEM | |
| managedStore::KeyType key( | |
| managedStore::ManagedType::kManagedAssociatedSubtreePaths, | |
| "/xyz/openbmc_project/inventory/system/board/platform1/contained_by", | |
| "/xyz/openbmc_project/inventory", 0, | |
| {"xyz.openbmc_project.Inventory.Item.System"}); | |
| std::vector<std::string> mockAssocPaths = { | |
| "/xyz/openbmc_project/inventory/system1"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| key, managedStore::MockManagedStoreTest::CreateValueType( | |
| mockAssocPaths)) | |
| .ok()); | |
| managedStore::KeyType processorKey( | |
| managedStore::ManagedType::kManagedProperty, | |
| "xyz.openbmc_project.ObjectMapper", | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/inventory/system/board/platform1/processors"), | |
| "xyz.openbmc_project.Association", "endpoints"); | |
| std::vector<std::string> mockProcessors = { | |
| "/xyz/openbmc_project/inventory/system1/chassis/processors/cpu0", | |
| "/xyz/openbmc_project/inventory/system1/chassis/processors/cpu1"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| processorKey, | |
| managedStore::MockManagedStoreTest::CreateValueType( | |
| dbus::utility::DbusVariantType(mockProcessors))) | |
| .ok()); | |
| std::string expectedSystem = "/redfish/v1/Systems/system1"; | |
| #else | |
| std::string expectedSystem = "/redfish/v1/Systems/system"; | |
| #endif | |
| managedStore::KeyType powerStateKey( | |
| managedStore::ManagedType::kManagedProperty, | |
| "xyz.openbmc_project.State.Chassis1", | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/state/chassis_system0"), | |
| "xyz.openbmc_project.State.Chassis", "CurrentPowerState"); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| powerStateKey, | |
| managedStore::MockManagedStoreTest::CreateValueType( | |
| dbus::utility::DbusVariantType(std::string( | |
| "xyz.openbmc_project.State.Chassis.PowerState.On")))) | |
| .ok()); | |
| handleChassisGet(app_, CreateRequest(), share_async_resp_, "platform1"); | |
| RunIoUntilDone(); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["@odata.id"], "/redfish/v1/Chassis/platform1"); | |
| EXPECT_EQ(json["@odata.type"], "#Chassis.v1_17_0.Chassis"); | |
| EXPECT_EQ(json["Actions"]["#Chassis.Reset"]["@Redfish.ActionInfo"], | |
| "/redfish/v1/Chassis/platform1/ResetActionInfo"); | |
| EXPECT_EQ(json["Actions"]["#Chassis.Reset"]["target"], | |
| "/redfish/v1/Chassis/platform1/Actions/Chassis.Reset"); | |
| EXPECT_EQ(json["Assembly"]["@odata.id"], | |
| "/redfish/v1/Chassis/platform1/Assembly"); | |
| EXPECT_EQ(json["Certificates"]["@odata.id"], | |
| "/redfish/v1/Chassis/platform1/Certificates"); | |
| EXPECT_EQ(json["ChassisType"], "RackMount"); | |
| EXPECT_EQ(json["Drives"]["@odata.id"], | |
| "/redfish/v1/Chassis/platform1/Drives"); | |
| #ifdef BMCWEB_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM | |
| EXPECT_EQ(json["EnvironmentMetrics"]["@odata.id"], | |
| "/redfish/v1/Chassis/platform1/EnvironmentMetrics"); | |
| #endif | |
| EXPECT_EQ(json["Id"], "platform1"); | |
| EXPECT_EQ(json["Links"]["Cables"].size(), | |
| json["Links"]["Cables@odata.count"]); | |
| EXPECT_EQ(json["Links"]["Cables"][0]["@odata.id"], | |
| "/redfish/v1/Cables/NcsiCable"); | |
| EXPECT_EQ(json["Links"]["ComputerSystems"].size(), 1); | |
| EXPECT_EQ(json["Links"]["ComputerSystems"][0]["@odata.id"], expectedSystem); | |
| EXPECT_EQ(json["Links"]["ManagedBy"].size(), 1); | |
| EXPECT_EQ(json["Links"]["ManagedBy"][0]["@odata.id"], | |
| "/redfish/v1/Managers/bmc"); | |
| EXPECT_EQ(json["Links"]["Processors"].size(), | |
| json["Links"]["Processors@odata.count"]); | |
| #ifdef BMCWEB_ALLOW_NON_HARDCODED_SINGLEHOST_SYSTEM | |
| CheckChassisProcessorsAndSubsystems(json, expectedSystem, false); | |
| #else | |
| CheckChassisProcessorsAndSubsystems(json, expectedSystem, true); | |
| #endif | |
| EXPECT_EQ(json["Manufacturer"], "Manufacturer"); | |
| EXPECT_EQ(json["Model"], "Model"); | |
| EXPECT_EQ(json["Name"], "platform1"); | |
| EXPECT_EQ(json["PCIeSlots"]["@odata.id"], | |
| "/redfish/v1/Chassis/platform1/PCIeSlots"); | |
| EXPECT_EQ(json["PartNumber"], "PartNumber"); | |
| EXPECT_EQ(json["PowerState"], "On"); | |
| #ifdef BMCWEB_ALLOW_DEPRECATED_POWER_THERMAL | |
| EXPECT_EQ(json["Power"]["@odata.id"], "/redfish/v1/Chassis/platform1/Power"); | |
| #endif | |
| #ifdef BMCWEB_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM | |
| EXPECT_EQ(json["PowerSubsystem"]["@odata.id"], | |
| "/redfish/v1/Chassis/platform1/PowerSubsystem"); | |
| #endif | |
| EXPECT_EQ(json["Sensors"]["@odata.id"], | |
| "/redfish/v1/Chassis/platform1/Sensors"); | |
| EXPECT_EQ(json["SerialNumber"], "SerialNumber"); | |
| EXPECT_EQ(json["Status"]["State"], "Enabled"); | |
| #ifdef BMCWEB_ALLOW_DEPRECATED_POWER_THERMAL | |
| EXPECT_EQ(json["Thermal"]["@odata.id"], | |
| "/redfish/v1/Chassis/platform1/Thermal"); | |
| #endif | |
| #ifdef BMCWEB_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM | |
| EXPECT_EQ(json["ThermalSubsystem"]["@odata.id"], | |
| "/redfish/v1/Chassis/platform1/ThermalSubsystem"); | |
| #endif | |
| EXPECT_EQ(json["TrustedComponents"]["@odata.id"], | |
| "/redfish/v1/Chassis/platform1/TrustedComponents"); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| } | |
| TEST_F(SnapshotFixture, GetChassisTestPowerStateOff) { | |
| #ifdef BMCWEB_ALLOW_NON_HARDCODED_SINGLEHOST_SYSTEM | |
| managedStore::KeyType key( | |
| managedStore::ManagedType::kManagedAssociatedSubtreePaths, | |
| "/xyz/openbmc_project/inventory/system/board/platform1/contained_by", | |
| "/xyz/openbmc_project/inventory", 0, | |
| {"xyz.openbmc_project.Inventory.Item.System"}); | |
| std::vector<std::string> mockAssocPaths = { | |
| "/xyz/openbmc_project/inventory/system1"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| key, managedStore::MockManagedStoreTest::CreateValueType( | |
| mockAssocPaths)) | |
| .ok()); | |
| managedStore::KeyType processorKey( | |
| managedStore::ManagedType::kManagedProperty, | |
| "xyz.openbmc_project.ObjectMapper", | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/inventory/system/board/platform1/processors"), | |
| "xyz.openbmc_project.Association", "endpoints"); | |
| std::vector<std::string> mockProcessors = { | |
| "/xyz/openbmc_project/inventory/system1/chassis/processors/cpu0", | |
| "/xyz/openbmc_project/inventory/system1/chassis/processors/cpu1"}; | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| processorKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| dbus::utility::DbusVariantType(mockProcessors))) | |
| .ok()); | |
| #endif | |
| managedStore::KeyType powerStateKey( | |
| managedStore::ManagedType::kManagedProperty, | |
| "xyz.openbmc_project.State.Chassis1", | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/state/chassis_system0"), | |
| "xyz.openbmc_project.State.Chassis", "CurrentPowerState"); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| powerStateKey, | |
| managedStore::MockManagedStoreTest::CreateValueType( | |
| dbus::utility::DbusVariantType(std::string( | |
| "xyz.openbmc_project.State.Chassis.PowerState.Off")))) | |
| .ok()); | |
| handleChassisGet(app_, CreateRequest(), share_async_resp_, "platform1"); | |
| RunIoUntilDone(); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["PowerState"], "Off"); | |
| EXPECT_EQ(json["Status"]["State"], "StandbyOffline"); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| // Restore power state to On for clean state in fixture | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| powerStateKey, | |
| managedStore::MockManagedStoreTest::CreateValueType( | |
| dbus::utility::DbusVariantType(std::string( | |
| "xyz.openbmc_project.State.Chassis.PowerState.On")))) | |
| .ok()); | |
| } | |
| TEST_F(SnapshotFixture, PostChassisCycleDelayGood) { | |
| SubprocessUtilsFake subprocessUtilsFake; | |
| SubprocessUtilsBase::setInstance( | |
| std::make_unique<SubprocessUtilsFake>(subprocessUtilsFake)); | |
| handleChassisResetActionInfoPost( | |
| app_, CreateRequest("{\"ResetType\":\"PowerCycle\", \"Delay\":1} "), | |
| share_async_resp_, "platform1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["ResetString"], | |
| "systemd-run --on-active=1 --timer-property=AccuracySec=100ms -- " | |
| "systemctl start gbmc-psu-hardreset.target"); | |
| } | |
| TEST_F(SnapshotFixture, PostChassisCycleDelayBadResetType) { | |
| handleChassisResetActionInfoPost( | |
| app_, CreateRequest("{\"ResetType\":\"PleaseReset\", \"Delay\":1} "), | |
| share_async_resp_, "platform1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::bad_request); | |
| } | |
| TEST_F(SnapshotFixture, PostChassisCycleWithoutDelay) { | |
| handleChassisResetActionInfoPost( | |
| app_, CreateRequest("{\"ResetType\":\"PowerCycle\"} "), share_async_resp_, | |
| "platform1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| } | |
| TEST_F(SnapshotFixture, PostChassisCycleDelayBadTime) { | |
| handleChassisResetActionInfoPost( | |
| app_, CreateRequest("{\"ResetType\":\"PowerCycle\", \"Delay\":3601} "), | |
| share_async_resp_, "platform1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::bad_request); | |
| } | |
| TEST_F(SnapshotFixture, PostChassisCycleInvalidParam) { | |
| handleChassisResetActionInfoPost( | |
| app_, | |
| CreateRequest("{\"ResetType\":\"PowerCycle\", \"UnknownParm_foo\":30} "), | |
| share_async_resp_, "platform1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::bad_request); | |
| } | |
| } // namespace | |
| } // namespace redfish |