blob: 4e54ffd74c71d4d834f9bf390130136d0ae9f1ce [file] [log] [blame] [edit]
#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