| #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, 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 |