| #include "async_resp.hpp" |
| #include "google/google_service_nvme.hpp" |
| #include "http_request.hpp" |
| #include "nlohmann/json.hpp" |
| #include "test/g3/mock_managed_store_test.hpp" |
| |
| #include <sdbusplus/test/sdbus_mock.hpp> |
| |
| #include <gtest/gtest.h> |
| |
| namespace crow::google_api |
| { |
| namespace |
| { |
| |
| using ::managedStore::KeyType; |
| using ::managedStore::ManagedType; |
| using ::testing::_; |
| |
| class GoogleServiceNvmeTest : public ::managedStore::MockManagedStoreTest |
| { |
| protected: |
| GoogleServiceNvmeTest() : app_(false) |
| { |
| share_async_resp_ = std::make_shared<bmcweb::AsyncResp>(); |
| } |
| |
| void SetUp() override |
| { |
| ::managedStore::MockManagedStoreTest::SetUp(); |
| |
| // Mock NVMe Controller object |
| KeyType nvmeKey(ManagedType::kManagedSubtree, |
| "/xyz/openbmc_project/inventory", 0, |
| {"xyz.openbmc_project.Inventory.Item.Drive"}); |
| |
| dbus::utility::MapperGetSubTreeResponse subtree = { |
| {nvmePath, |
| {{"xyz.openbmc_project.Inventory.Item.Drive", |
| {"xyz.openbmc_project.Inventory.Item.Drive"}}}}}; |
| |
| EXPECT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore( |
| nvmeKey, |
| managedStore::MockManagedStoreTest::CreateValueType( |
| subtree)) |
| .ok()); |
| |
| // Mock Drive Protocol |
| KeyType protocolKey(ManagedType::kManagedProperty, |
| "xyz.openbmc_project.Inventory.Item.Drive", |
| sdbusplus::message::object_path(nvmePath), |
| "xyz.openbmc_project.Inventory.Item.Drive", |
| "Protocol"); |
| |
| dbus::utility::DbusVariantType protocolVariant = std::string( |
| "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe"); |
| EXPECT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore( |
| protocolKey, |
| managedStore::MockManagedStoreTest::CreateValueType( |
| protocolVariant)) |
| .ok()); |
| |
| // Mock storage controller association |
| KeyType storageControllerKey( |
| ManagedType::kManagedProperty, "xyz.openbmc_project.ObjectMapper", |
| sdbusplus::message::object_path(nvmePath + "/storage_controller"), |
| "xyz.openbmc_project.Association", "endpoints"); |
| |
| std::vector<std::string> endpoints = {controllerPath}; |
| dbus::utility::DbusVariantType endpointsVariant = endpoints; |
| |
| EXPECT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore( |
| storageControllerKey, |
| managedStore::MockManagedStoreTest::CreateValueType( |
| endpointsVariant)) |
| .ok()); |
| |
| // Mock Lockdown Interface |
| KeyType lockdownKey(ManagedType::kManagedMapperObject, controllerPath, |
| {"xyz.openbmc_project.NVMe.Lockdown"}); |
| dbus::utility::MapperGetObject lockdownObject = { |
| {"xyz.openbmc_project.NVMe.Lockdown", |
| {{"xyz.openbmc_project.NVMe.Lockdown", {}}}}}; |
| |
| EXPECT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore( |
| lockdownKey, |
| managedStore::MockManagedStoreTest::CreateValueType( |
| lockdownObject)) |
| .ok()); |
| } |
| |
| crow::Request CreateRequest(std::string_view body) |
| { |
| std::error_code ec; |
| boost::beast::http::request<boost::beast::http::string_body> beastReq; |
| beastReq.method(boost::beast::http::verb::post); |
| beastReq.target( |
| "/google/v1/NVMe/nvme_0/Controllers/controller_0/Actions/NVMeController.LockdownInband"); |
| beastReq.body() = std::string(body); |
| beastReq.prepare_payload(); |
| return crow::Request(beastReq, ec); |
| } |
| |
| std::shared_ptr<bmcweb::AsyncResp> share_async_resp_; |
| App app_; |
| std::string nvmeId = "nvme_0"; |
| std::string controllerId = "controller_0"; |
| std::string controllerPath = |
| "/xyz/openbmc_project/inventory/system/board/nvme_0/controller_0"; |
| std::string nvmePath = "/xyz/openbmc_project/inventory/system/board/nvme_0"; |
| }; |
| |
| class SimulateSuccessfulAsyncPostDbusCallThreadSafeWithMsgAndValueAction |
| { |
| const std::tuple<uint32_t, uint32_t, uint32_t, std::string, uint32_t> |
| value_; |
| |
| public: |
| explicit SimulateSuccessfulAsyncPostDbusCallThreadSafeWithMsgAndValueAction( |
| const std::tuple<uint32_t, uint32_t, uint32_t, std::string, uint32_t>& |
| value) : value_(value) |
| {} |
| |
| template <typename Result, typename ArgumentTuple> |
| Result Perform(const ArgumentTuple& args) const |
| { |
| boost::asio::post( |
| managedStore::GetManagedObjectStore()->GetIoContext(), |
| [callback{std::move(std::get<1>(args))}, this]() mutable { |
| // Mock sdbus message |
| testing::NiceMock<sdbusplus::SdBusMock> sdbus; |
| EXPECT_CALL(sdbus, sd_bus_message_ref(testing::_)) |
| .WillRepeatedly(testing::Return(nullptr)); |
| EXPECT_CALL(sdbus, sd_bus_message_get_error(testing::_)) |
| .WillRepeatedly(testing::Return(nullptr)); |
| |
| sdbusplus::message_t msg = |
| sdbusplus::get_mocked_new(&sdbus).new_method_call( |
| nullptr, nullptr, nullptr, nullptr); |
| |
| std::move(callback)(boost::system::error_code(), msg, this->value_); |
| }); |
| } |
| |
| static testing::PolymorphicAction< |
| SimulateSuccessfulAsyncPostDbusCallThreadSafeWithMsgAndValueAction> |
| SimulateSuccessfulAsyncPostDbusCallWithMsgAndValue( |
| const std::tuple<uint32_t, uint32_t, uint32_t, std::string, |
| uint32_t>& value) |
| { |
| return testing::MakePolymorphicAction( |
| SimulateSuccessfulAsyncPostDbusCallThreadSafeWithMsgAndValueAction( |
| value)); |
| } |
| }; |
| |
| TEST_F(GoogleServiceNvmeTest, LockdownInband_ProhibitFalse) |
| { |
| EXPECT_CALL( |
| *managedStore::GetManagedObjectStore(), |
| PostDbusCallToIoContextThreadSafe( |
| _, |
| testing::An<absl::AnyInvocable<void( |
| const boost::system::error_code&, const sdbusplus::message_t&, |
| const std::tuple<uint32_t, uint32_t, uint32_t, std::string, |
| uint32_t>&)>&&>(), |
| "xyz.openbmc_project.NVMe.Lockdown", controllerPath, |
| "xyz.openbmc_project.NVMe.Lockdown", "LockdownInband", 0, |
| std::vector<uint8_t>{}, std::vector<uint8_t>{}, |
| std::vector<uint8_t>{})) |
| .Times(1) |
| .WillOnce( |
| SimulateSuccessfulAsyncPostDbusCallThreadSafeWithMsgAndValueAction:: |
| SimulateSuccessfulAsyncPostDbusCallWithMsgAndValue( |
| std::make_tuple(0, 0, 0, "", 0))); |
| |
| crow::Request req = CreateRequest(R"({"Prohibit": false})"); |
| |
| handleGoogleNvmeControllerLockdownInband(app_, req, share_async_resp_, |
| nvmeId, controllerId); |
| |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); |
| } |
| |
| TEST_F(GoogleServiceNvmeTest, LockdownInband_ProhibitTrue) |
| { |
| EXPECT_CALL( |
| *managedStore::GetManagedObjectStore(), |
| PostDbusCallToIoContextThreadSafe( |
| _, |
| testing::An<absl::AnyInvocable<void( |
| const boost::system::error_code&, const sdbusplus::message_t&, |
| const std::tuple<uint32_t, uint32_t, uint32_t, std::string, |
| uint32_t>&)>&&>(), |
| "xyz.openbmc_project.NVMe.Lockdown", controllerPath, |
| "xyz.openbmc_project.NVMe.Lockdown", "LockdownInband", 1, |
| std::vector<uint8_t>{}, std::vector<uint8_t>{}, |
| std::vector<uint8_t>{})) |
| .Times(1) |
| .WillOnce( |
| SimulateSuccessfulAsyncPostDbusCallThreadSafeWithMsgAndValueAction:: |
| SimulateSuccessfulAsyncPostDbusCallWithMsgAndValue( |
| std::make_tuple(0, 0, 0, "", 0))); |
| |
| crow::Request req = CreateRequest(R"({"Prohibit": true})"); |
| |
| handleGoogleNvmeControllerLockdownInband(app_, req, share_async_resp_, |
| nvmeId, controllerId); |
| |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); |
| } |
| |
| TEST_F(GoogleServiceNvmeTest, LockdownInband_WithAdminCmds) |
| { |
| std::vector<uint8_t> expectedAdminCmds = {0x1, 0x2, 0x3}; |
| |
| EXPECT_CALL( |
| *managedStore::GetManagedObjectStore(), |
| PostDbusCallToIoContextThreadSafe( |
| _, |
| testing::An<absl::AnyInvocable<void( |
| const boost::system::error_code&, const sdbusplus::message_t&, |
| const std::tuple<uint32_t, uint32_t, uint32_t, std::string, |
| uint32_t>&)>&&>(), |
| "xyz.openbmc_project.NVMe.Lockdown", controllerPath, |
| "xyz.openbmc_project.NVMe.Lockdown", "LockdownInband", 0, |
| expectedAdminCmds, std::vector<uint8_t>{}, std::vector<uint8_t>{})) |
| .Times(1) |
| .WillOnce( |
| SimulateSuccessfulAsyncPostDbusCallThreadSafeWithMsgAndValueAction:: |
| SimulateSuccessfulAsyncPostDbusCallWithMsgAndValue( |
| std::make_tuple(0, 0, 0, "", 0))); |
| |
| crow::Request req = |
| CreateRequest(R"({"Prohibit": false, "AdminCmds": [1, 2, 3]})"); |
| |
| handleGoogleNvmeControllerLockdownInband(app_, req, share_async_resp_, |
| nvmeId, controllerId); |
| |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); |
| } |
| |
| TEST_F(GoogleServiceNvmeTest, LockdownInband_AllParams) |
| { |
| std::vector<uint8_t> expectedAdminCmds = {0xA}; |
| std::vector<uint8_t> expectedFeatures = {0xB, 0xC}; |
| std::vector<uint8_t> expectedLogPages = {0xD}; |
| |
| EXPECT_CALL( |
| *managedStore::GetManagedObjectStore(), |
| PostDbusCallToIoContextThreadSafe( |
| _, |
| testing::An<absl::AnyInvocable<void( |
| const boost::system::error_code&, const sdbusplus::message_t&, |
| const std::tuple<uint32_t, uint32_t, uint32_t, std::string, |
| uint32_t>&)>&&>(), |
| "xyz.openbmc_project.NVMe.Lockdown", controllerPath, |
| "xyz.openbmc_project.NVMe.Lockdown", "LockdownInband", 1, |
| expectedAdminCmds, expectedFeatures, expectedLogPages)) |
| .Times(1) |
| .WillOnce( |
| SimulateSuccessfulAsyncPostDbusCallThreadSafeWithMsgAndValueAction:: |
| SimulateSuccessfulAsyncPostDbusCallWithMsgAndValue( |
| std::make_tuple(0, 0, 0, "", 0))); |
| |
| crow::Request req = CreateRequest( |
| R"({"Prohibit": true, "AdminCmds": [10], "Features": [11, 12], "LogPages": [13]})"); |
| |
| handleGoogleNvmeControllerLockdownInband(app_, req, share_async_resp_, |
| nvmeId, controllerId); |
| |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); |
| } |
| |
| TEST_F(GoogleServiceNvmeTest, LockdownInband_DbusError) |
| { |
| testing::NiceMock<sdbusplus::SdBusMock> sdbus; |
| sd_bus_error err = SD_BUS_ERROR_NULL; |
| err.name = "org.freedesktop.DBus.Error.Failed"; |
| err.message = "D-Bus call failed"; |
| |
| EXPECT_CALL(sdbus, sd_bus_message_get_error(testing::_)) |
| .WillRepeatedly(testing::Return(&err)); |
| EXPECT_CALL(sdbus, sd_bus_message_ref(testing::_)) |
| .WillRepeatedly(testing::Return(nullptr)); |
| |
| sdbusplus::message_t msg = |
| sdbusplus::get_mocked_new(&sdbus).new_method_call(nullptr, nullptr, |
| nullptr, nullptr); |
| |
| EXPECT_CALL( |
| *managedStore::GetManagedObjectStore(), |
| PostDbusCallToIoContextThreadSafe( |
| _, |
| testing::An<absl::AnyInvocable<void( |
| const boost::system::error_code&, const sdbusplus::message_t&, |
| const std::tuple<uint32_t, uint32_t, uint32_t, std::string, |
| uint32_t>&)>&&>(), |
| _, _, _, _, _, _, _, _)) |
| .Times(1) |
| .WillOnce( |
| managedStore:: |
| SimulateFailedAsyncPostDbusCallThreadSafeWithMsgAndEmptyValueAction:: |
| SimulateFailedAsyncPostDbusCallWithMsgAndEmptyValue(msg)); |
| |
| crow::Request req = CreateRequest(R"({"Prohibit": false})"); |
| |
| handleGoogleNvmeControllerLockdownInband(app_, req, share_async_resp_, |
| nvmeId, controllerId); |
| |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(share_async_resp_->res.result(), |
| boost::beast::http::status::internal_server_error); |
| EXPECT_EQ(share_async_resp_->res.jsonValue["error"]["message"], |
| "D-Bus call failed"); |
| } |
| |
| TEST_F(GoogleServiceNvmeTest, LockdownInband_BoostError) |
| { |
| testing::NiceMock<sdbusplus::SdBusMock> sdbus; |
| EXPECT_CALL(sdbus, sd_bus_message_get_error(testing::_)) |
| .WillRepeatedly(testing::Return(nullptr)); |
| EXPECT_CALL(sdbus, sd_bus_message_ref(testing::_)) |
| .WillRepeatedly(testing::Return(nullptr)); |
| |
| sdbusplus::message_t msg = |
| sdbusplus::get_mocked_new(&sdbus).new_method_call(nullptr, nullptr, |
| nullptr, nullptr); |
| |
| EXPECT_CALL( |
| *managedStore::GetManagedObjectStore(), |
| PostDbusCallToIoContextThreadSafe( |
| _, |
| testing::An<absl::AnyInvocable<void( |
| const boost::system::error_code&, const sdbusplus::message_t&, |
| const std::tuple<uint32_t, uint32_t, uint32_t, std::string, |
| uint32_t>&)>&&>(), |
| _, _, _, _, _, _, _, _)) |
| .Times(1) |
| .WillOnce( |
| [msg{std::move(msg)}](auto&&, auto&& callback, auto&&...) mutable { |
| boost::asio::post( |
| managedStore::GetManagedObjectStore()->GetIoContext(), |
| [callback{std::move(callback)}, msg{std::move(msg)}]() mutable { |
| std::move(callback)(boost::system::errc::make_error_code( |
| boost::system::errc::not_connected), |
| msg, {}); |
| }); |
| }); |
| |
| crow::Request req = CreateRequest(R"({"Prohibit": false})"); |
| |
| handleGoogleNvmeControllerLockdownInband(app_, req, share_async_resp_, |
| nvmeId, controllerId); |
| |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(share_async_resp_->res.result(), |
| boost::beast::http::status::internal_server_error); |
| } |
| |
| } // namespace |
| } // namespace crow::google_api |