blob: cabe6e0e175f3509604cf022e742e926341e4021 [file]
#include "google/google_service_nvme.hpp"
#include <systemd/sd-bus.h>
#include <cstdint>
#include <memory>
#include <string>
#include <string_view>
#include <system_error> // NOLINT
#include <tuple>
#include <utility>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/functional/any_invocable.h"
#include "boost/asio/post.hpp" // NOLINT
#include "app.hpp"
#include "http_request.hpp"
#include "async_resp.hpp"
#include "dbus_utility.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.hpp"
#include "sdbusplus/message/native_types.hpp"
#include "sdbusplus/test/sdbus_mock.hpp"
namespace crow::google_api {
namespace {
using ::managedStore::KeyType;
using ::managedStore::ManagedType;
using ::testing::_;
using ::testing::An;
using ::testing::MakePolymorphicAction;
using ::testing::NiceMock;
using ::testing::PolymorphicAction;
using ::testing::Return;
constexpr std::string_view kNvmeId = "nvme_0";
constexpr std::string_view kControllerId = "controller_0";
constexpr std::string_view kControllerPath =
"/xyz/openbmc_project/inventory/system/board/nvme_0/controller_0";
constexpr std::string_view kNvmePath =
"/xyz/openbmc_project/inventory/system/board/nvme_0";
class GoogleServiceNvmeTest : public ::managedStore::MockManagedStoreTest {
protected:
GoogleServiceNvmeTest() : app_(true) {
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 = {
{std::string(kNvmePath),
{{"xyz.openbmc_project.Inventory.Item.Drive",
{"xyz.openbmc_project.Inventory.Item.Drive"}}}}};
EXPECT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
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(std::string(kNvmePath)),
"xyz.openbmc_project.Inventory.Item.Drive", "Protocol");
dbus::utility::DbusVariantType protocolVariant = std::string(
"xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe");
EXPECT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
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(std::string(kNvmePath) +
"/storage_controller"),
"xyz.openbmc_project.Association", "endpoints");
std::vector<std::string> endpoints = {std::string(kControllerPath)};
dbus::utility::DbusVariantType endpointsVariant = endpoints;
EXPECT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
storageControllerKey,
managedStore::MockManagedStoreTest::CreateValueType(
endpointsVariant))
.ok());
// Mock Lockdown Interface
KeyType lockdownKey(ManagedType::kManagedMapperObject,
std::string(kControllerPath),
{"xyz.openbmc_project.NVMe.Lockdown"});
dbus::utility::MapperGetObject lockdownObject = {
{"xyz.openbmc_project.NVMe.Lockdown",
{{"xyz.openbmc_project.NVMe.Lockdown", {}}}}};
EXPECT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
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.set(boost::beast::http::field::content_type, "application/json");
beastReq.body() = std::string(body);
beastReq.prepare_payload();
return crow::Request(beastReq, ec);
}
std::shared_ptr<bmcweb::AsyncResp> share_async_resp_;
App app_;
};
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 {
auto callback = std::make_shared<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>&)>>(
std::move(std::get<1>(const_cast<ArgumentTuple&>(args))));
boost::asio::post(
managedStore::GetManagedObjectStore()->GetIoContext(),
[callback, val = value_]() mutable {
// Mock sdbus message
auto sdbus = std::make_shared<NiceMock<sdbusplus::SdBusMock>>();
EXPECT_CALL(*sdbus, sd_bus_message_ref(_))
.WillRepeatedly(Return(nullptr));
EXPECT_CALL(*sdbus, sd_bus_message_get_error(_))
.WillRepeatedly(Return(nullptr));
sdbusplus::message_t msg(nullptr, sdbus.get());
std::move (*callback)(boost::system::error_code(), msg, val);
});
}
static PolymorphicAction<
SimulateSuccessfulAsyncPostDbusCallThreadSafeWithMsgAndValueAction>
SimulateSuccessfulAsyncPostDbusCallWithMsgAndValue(
const std::tuple<uint32_t, uint32_t, uint32_t, std::string, uint32_t>&
value) {
return MakePolymorphicAction(
SimulateSuccessfulAsyncPostDbusCallThreadSafeWithMsgAndValueAction(
value));
}
};
TEST_F(GoogleServiceNvmeTest, LockdownInbandProhibitFalse) {
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
_,
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", std::string(kControllerPath),
"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(uint32_t{0}, uint32_t{0}, uint32_t{0},
std::string{""}, uint32_t{0})));
crow::Request req = CreateRequest(R"({"Prohibit": false})");
handleGoogleNvmeControllerLockdownInband(app_, req, share_async_resp_,
std::string(kNvmeId),
std::string(kControllerId));
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(GoogleServiceNvmeTest, LockdownInbandProhibitTrue) {
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
_,
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", std::string(kControllerPath),
"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_,
std::string(kNvmeId),
std::string(kControllerId));
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(GoogleServiceNvmeTest, LockdownInbandWithAdminCmds) {
std::vector<uint8_t> expectedAdminCmds = {0x1, 0x2, 0x3};
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
_,
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", std::string(kControllerPath),
"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_,
std::string(kNvmeId),
std::string(kControllerId));
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(GoogleServiceNvmeTest, LockdownInbandAllParams) {
std::vector<uint8_t> expectedAdminCmds = {0xA};
std::vector<uint8_t> expectedFeatures = {0xB, 0xC};
std::vector<uint8_t> expectedLogPages = {0xD};
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
_,
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", std::string(kControllerPath),
"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_,
std::string(kNvmeId),
std::string(kControllerId));
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(GoogleServiceNvmeTest, LockdownInbandDbusError) {
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(_)).WillRepeatedly(Return(&err));
EXPECT_CALL(sdbus, sd_bus_message_ref(_)).WillRepeatedly(Return(nullptr));
sdbusplus::message_t msg(nullptr, &sdbus);
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
_,
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](auto&&, auto&& callback, auto&&...) mutable {
auto callback_in = std::make_shared<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>&)>>(
std::forward<decltype(callback)>(callback));
boost::asio::post(managedStore::GetManagedObjectStore()->GetIoContext(),
[callback_in, msg]() mutable {
std::move (*callback_in)(
boost::system::error_code(), msg, {});
});
});
crow::Request req = CreateRequest(R"({"Prohibit": false})");
handleGoogleNvmeControllerLockdownInband(app_, req, share_async_resp_,
std::string(kNvmeId),
std::string(kControllerId));
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, LockdownInbandBoostError) {
NiceMock<sdbusplus::SdBusMock> sdbus;
EXPECT_CALL(sdbus, sd_bus_message_get_error(_))
.WillRepeatedly(Return(nullptr));
EXPECT_CALL(sdbus, sd_bus_message_ref(_)).WillRepeatedly(Return(nullptr));
sdbusplus::message_t msg(nullptr, &sdbus);
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
_,
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](auto&&, auto&& callback, auto&&...) mutable {
auto callback_in = std::make_shared<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>&)>>(
std::forward<decltype(callback)>(callback));
boost::asio::post(
managedStore::GetManagedObjectStore()->GetIoContext(),
[callback_in, msg]() mutable {
std::move (*callback_in)(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_,
std::string(kNvmeId),
std::string(kControllerId));
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
} // namespace
} // namespace crow::google_api