| #include "bmc/http_connection.h" |
| |
| #include <chrono> // NOLINT(build/c++11) |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "bmc/daemon_context_bmc.h" |
| #include "bmc/mock_server.h" |
| #include "bmc/redfish.h" |
| #include "bmc/state_monitor_bmc.h" |
| #include "safepower_agent.pb.h" |
| #include "safepower_agent_config.pb.h" |
| #include "state_updater.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/time/time.h" |
| #include "nlohmann/json.hpp" |
| #include "nlohmann/json_fwd.hpp" |
| |
| namespace safepower_agent { |
| namespace { |
| |
| using mock_server_test::TestBoostServer; |
| using ::testing::StartsWith; |
| |
| TEST(http, NoConnection) { |
| // DO NOT start the test sever |
| DaemonContextBMC context; // only use DaemonContextBMC::Get() |
| |
| auto my_connection = std::make_shared<HttpConnection>(); |
| bool callback_ran = false; |
| my_connection->PerformConnection( |
| (http::verb::post), "/redfish/v1/Systems/system/Actions/Power.Reset", |
| [&callback_ran](absl::StatusOr<nlohmann::json> jres) { |
| ASSERT_FALSE(jres.ok()); |
| ASSERT_EQ(jres.status(), |
| absl::UnavailableError(absl::StrCat( |
| "Failed to connect to BMC: ", "Connection refused"))); |
| callback_ran = true; |
| }, |
| {}); |
| |
| DaemonContextBMC::Get().get_io_context().run_for(std::chrono::seconds(1)); |
| EXPECT_TRUE(callback_ran); |
| } |
| |
| TEST(http, BadAddress) { |
| DaemonContextBMC context; // only use DaemonContextBMC::Get() |
| mock_server_test::TestBoostServer my_server; |
| my_server.Start(DaemonContextBMC::Get().get_io_context()); |
| |
| auto my_connection = std::make_shared<HttpConnection>(absl::ZeroDuration()); |
| bool callback_ran = false; |
| my_connection->PerformConnection( |
| (http::verb::post), "/redfish/v1/Systems/system/Actions/Power.Reset", |
| [&callback_ran](absl::StatusOr<nlohmann::json> jres) { |
| ASSERT_TRUE(absl::IsUnavailable(jres.status())); |
| ASSERT_THAT(jres.status().message(), |
| StartsWith("Failed to connect to BMC: ")); |
| callback_ran = true; |
| }, |
| {}, "240.0.0.0", 80); |
| |
| DaemonContextBMC::Get().get_io_context().run_for(std::chrono::seconds(1)); |
| EXPECT_TRUE(callback_ran); |
| } |
| |
| TEST(http, WriteTimeOut) { |
| DaemonContextBMC context; // only use DaemonContextBMC::Get() |
| TestBoostServer my_server; |
| my_server.Start(DaemonContextBMC::Get().get_io_context()); |
| |
| auto my_connection = |
| std::make_shared<HttpConnection>(absl::Seconds(2), absl::ZeroDuration()); |
| bool callback_ran = false; |
| my_connection->PerformConnection( |
| (http::verb::post), |
| "/redfish/v1/Systems/system/Actions/Power.Reset_never_write", |
| [&callback_ran](absl::StatusOr<nlohmann::json> jres) { |
| ASSERT_FALSE(jres.ok()); |
| ASSERT_EQ(jres.status(), |
| absl::UnknownError("Callback error handler: The socket was " |
| "closed due to a timeout")); |
| callback_ran = true; |
| }, |
| {}); |
| DaemonContextBMC::Get().get_io_context().run_for(std::chrono::seconds(1)); |
| EXPECT_TRUE(callback_ran); |
| } |
| |
| TEST(http, MalFormedJson) { |
| DaemonContextBMC context; // only use DaemonContextBMC::Get() |
| TestBoostServer my_server; |
| my_server.Start(DaemonContextBMC::Get().get_io_context()); |
| |
| auto my_connection = std::make_shared<HttpConnection>(); |
| bool callback_ran = false; |
| my_connection->PerformConnection( |
| http::verb::post, |
| "/redfish/v1/Systems/system/Actions/Power.Reset_malformed", |
| [&callback_ran](absl::StatusOr<nlohmann::json> jres) { |
| ASSERT_FALSE(jres.ok()); |
| ASSERT_EQ(jres.status(), absl::InvalidArgumentError(absl::StrCat( |
| "Failed to parse JSON from BMC: ", |
| mock_server_test::malformedJson))); |
| callback_ran = true; |
| }, |
| {}); |
| DaemonContextBMC::Get().get_io_context().run_for(std::chrono::seconds(1)); |
| EXPECT_TRUE(callback_ran); |
| } |
| |
| TEST(http, NotFound) { |
| DaemonContextBMC context; // only use DaemonContextBMC::Get() |
| TestBoostServer my_server; |
| my_server.Start(DaemonContextBMC::Get().get_io_context()); |
| |
| auto my_connection = std::make_shared<HttpConnection>(); |
| bool callback_ran = false; |
| my_connection->PerformConnection( |
| http::verb::post, |
| "/redfish/v1/Systems/system/Actions/Power.Reset_NotFound", |
| [&callback_ran](absl::StatusOr<nlohmann::json> jres) { |
| ASSERT_FALSE(jres.ok()); |
| ASSERT_EQ(jres.status(), absl::UnavailableError( |
| "HTTP request failed with status: 404")); |
| callback_ran = true; |
| }, |
| {}); |
| DaemonContextBMC::Get().get_io_context().run_for(std::chrono::seconds(1)); |
| EXPECT_TRUE(callback_ran); |
| } |
| |
| TEST(http, GoodTest) { |
| DaemonContextBMC context; // only use DaemonContextBMC::Get() |
| auto my_server = std::make_shared<TestBoostServer>(); |
| my_server->Start(DaemonContextBMC::Get().get_io_context()); |
| |
| auto my_connection = std::make_shared<HttpConnection>(); |
| bool callback_ran = false; |
| my_connection->PerformConnection( |
| http::verb::post, "/redfish/v1/Systems/system/Actions/Power.Reset", |
| [&callback_ran](absl::StatusOr<nlohmann::json> jres) { |
| ASSERT_TRUE(jres.ok()); |
| callback_ran = true; |
| nlohmann::json good = nlohmann::json::parse(mock_server_test::goodJson); |
| ASSERT_EQ(jres.value(), good); |
| }, |
| {}); |
| |
| DaemonContextBMC::Get().get_io_context().run_for(std::chrono::seconds(1)); |
| EXPECT_TRUE(callback_ran); |
| } |
| |
| // redfish Tests |
| // redfish and http test both use mock server |
| // but they can not run at the same time |
| |
| TEST(http, GoodGet) { |
| DaemonContextBMC context; // only use DaemonContextBMC::Get() |
| TestBoostServer my_server; |
| my_server.Start(DaemonContextBMC::Get().get_io_context()); |
| bool callback_ran = false; |
| Redfish::Get("/redfish/v1/Systems/system_get", |
| [&callback_ran](absl::StatusOr<nlohmann::json> jres) { |
| ASSERT_TRUE(jres.ok()); |
| nlohmann::json good = |
| nlohmann::json::parse(mock_server_test::goodSystemGet); |
| ASSERT_EQ(jres.value(), good); |
| callback_ran = true; |
| }); |
| |
| DaemonContextBMC::Get().get_io_context().run_for(std::chrono::seconds(1)); |
| EXPECT_TRUE(callback_ran); |
| } |
| |
| TEST(http, BadGet) { |
| DaemonContextBMC context; // only use DaemonContextBMC::Get() |
| TestBoostServer my_server; |
| my_server.Start(DaemonContextBMC::Get().get_io_context()); |
| bool callback_ran = false; |
| Redfish::Get("/redfish/v1/Systems/system_NotFound", |
| [&callback_ran](absl::StatusOr<nlohmann::json> jres) { |
| ASSERT_FALSE(jres.ok()); |
| ASSERT_EQ(jres.status(), |
| absl::UnavailableError( |
| "HTTP request failed with status: 404")); |
| callback_ran = true; |
| }); |
| DaemonContextBMC::Get().get_io_context().run_for(std::chrono::seconds(1)); |
| EXPECT_TRUE(callback_ran); |
| } |
| |
| using safepower_agent_config::GpowerdConfig; |
| |
| TEST(StateMonitorBMC, GoodBuild) { |
| DaemonContextBMC context; // only use DaemonContextBMC::Get() |
| TestBoostServer my_server; |
| my_server.Start(DaemonContextBMC::Get().get_io_context()); |
| |
| GpowerdConfig gpower_config; |
| auto state_monitor_config = gpower_config.add_state_monitor_config(); |
| state_monitor_config->set_state_name("test_state"); |
| state_monitor_config->mutable_state_gathering_info() |
| ->set_collection_interval_ms(100); |
| state_monitor_config->mutable_state_gathering_info() |
| ->mutable_redfish() |
| ->set_uri("/redfish/v1/Systems/system/_testingPowerState"); |
| |
| // if the config file has a key, it should also have the corresponding state |
| std::vector< |
| std::pair<std::string, safepower_agent_proto::PowerStateSpecifier>> |
| json_key_to_state_name = { |
| {"PowerState_On", safepower_agent_proto::POWER_STATE_ON}, |
| {"PowerState_Off", safepower_agent_proto::POWER_STATE_OFF}, |
| {"PowerState_Paused", safepower_agent_proto::POWER_STATE_PAUSED}, |
| {"PowerState_PoweringOn", |
| safepower_agent_proto::POWER_STATE_POWERING_ON}, |
| {"PowerState_PoweringOff", |
| safepower_agent_proto::POWER_STATE_POWERING_OFF}, |
| {"PowerState_UNSPECIFIED_1", |
| safepower_agent_proto::POWER_STATE_UNSPECIFIED}, |
| {"PowerState_UNSPECIFIED_2", |
| safepower_agent_proto::POWER_STATE_UNSPECIFIED}, |
| {"", safepower_agent_proto::POWER_STATE_UNSPECIFIED}, |
| {"b", safepower_agent_proto::POWER_STATE_UNSPECIFIED}}; |
| |
| for (auto str_state_pair : json_key_to_state_name) { |
| state_monitor_config->mutable_state_gathering_info() |
| ->mutable_redfish() |
| ->set_json_key(str_state_pair.first); |
| state_monitor_config->set_state_name(str_state_pair.first); |
| state_monitor_config->mutable_system_component()->set_node_entity_tag("cn"); |
| state_monitor_config->mutable_system_component()->set_name("PowerState"); |
| state_monitor_config->mutable_state_gathering_info() |
| ->mutable_prototype() |
| ->mutable_power_state() |
| ->set_state(safepower_agent_proto::POWER_STATE_POWERING_OFF); |
| // build a new system state |
| safepower_agent_proto::SystemState initialState; |
| // set default power state |
| safepower_agent_proto::PowerState ps; |
| ps.set_state(safepower_agent_proto::POWER_STATE_POWERING_OFF); |
| |
| safepower_agent_proto::ComponentState cs; |
| *(cs.mutable_power_state()) = ps; |
| |
| safepower_agent_proto::NodeState ns; |
| ns.mutable_component_state()->insert({"PowerState", cs}); |
| initialState.mutable_node_state()->insert({"cn", ns}); |
| |
| auto reactor = |
| std::make_shared<StateUpdater<safepower_agent_proto::SystemState>>( |
| std::move(initialState), true); |
| // monitor new systemd state, with specific key |
| StateMonitorBMC state_monitor(reactor); |
| ASSERT_EQ(state_monitor.BuildFromConfig(gpower_config), absl::OkStatus()); |
| DaemonContextBMC::Get().get_io_context().run_for( |
| std::chrono::milliseconds(300)); |
| // check the state monitor updates its reactor with the correct state |
| EXPECT_EQ(reactor->state() |
| .node_state() |
| .at("cn") |
| .component_state() |
| .at("PowerState") |
| .power_state() |
| .state(), |
| str_state_pair.second); |
| ASSERT_EQ(DaemonContextBMC::Get().scheduler().CancelAll(), |
| absl::OkStatus()); |
| DaemonContextBMC::Get().get_io_context().run_for( |
| std::chrono::milliseconds(300)); |
| } |
| |
| DaemonContextBMC::Get().get_io_context().run_for( |
| std::chrono::milliseconds(1000)); |
| } |
| |
| } // namespace |
| } // namespace safepower_agent |