blob: 6ab66fdebc0f45e1e89ea22c64134d0987b75076 [file]
#include <cstdint>
#include <filesystem> // NOLINT
#include <fstream>
#include <memory>
#include <string>
#include <system_error> // NOLINT
#include <vector>
#include <gmock/gmock.h> // IWYU pragma: keep
#include <gtest/gtest.h> // IWYU pragma: keep
#include "absl/cleanup/cleanup.h"
#include "absl/functional/any_invocable.h"
#include "absl/log/log.h"
#include "boost/system/error_code.hpp" // NOLINT
#include "bm_config.h"
#include "http_request.hpp"
#include "http_response.hpp"
#include "logging.hpp"
#include "async_resp.hpp"
#include "dbus_utility.hpp"
#include "bios.hpp"
#include "test/redfish-core/lib/snapshot_fixture.hpp"
#include <nlohmann/json.hpp>
#include "managed_store.hpp"
#include "test/g3/mock_managed_store.hpp"
// IWYU pragma: no_include <gtest/gtest-message.h>
// IWYU pragma: no_include <gtest/gtest-test-part.h>
// IWYU pragma: no_include "gtest/gtest_pred_impl.h"
// IWYU pragma: no_include <gmock/gmock-matchers.h>
// IWYU pragma: no_include <gtest/gtest-matchers.h>
namespace redfish {
namespace {
using ::dbus::utility::DbusVariantType;
using ::managedStore::SimulateFailedAsyncPostDbusCallThreadSafeAction;
using ::managedStore::SimulateSuccessfulAsyncPostDbusCallThreadSafeAction;
using ::testing::_;
using ::testing::An;
TEST_F(SnapshotFixture_Platform23,
MultiHostPlatformHandleBiosServiceGetTestNonExistSystem) {
handleBiosServiceGet(app_, CreateRequest(), share_async_resp_, "system3");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::not_found);
}
TEST_F(SnapshotFixture_Platform23,
MultiHostPlatformHandleBiosServiceGetTestReturnsNotFound) {
handleBiosServiceGet(app_, CreateRequest(), share_async_resp_, "system");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::not_found);
}
TEST_F(
SnapshotFixture_Platform23,
// NOLINTNEXTLINE(whitespace/line_length): Test name is too long.
MultiHostPlatformHandleBiosServiceGetTestReturnsCorrectResponseValidBlobPathWithNoBlobContent) {
crow::Logger::setLogLevel(crow::LogLevel::Error);
handleBiosServiceGet(app_, CreateRequest(), share_async_resp_, "system1");
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json["@odata.id"], "/redfish/v1/Systems/system1/Bios");
EXPECT_EQ(json["@odata.type"], "#Bios.v1_1_0.Bios");
EXPECT_EQ(json["Actions"]["#Bios.ResetBios"]["target"],
"/redfish/v1/Systems/system1/Bios/Actions/Bios.ResetBios");
EXPECT_EQ(json["Description"], "BIOS Configuration Service");
EXPECT_EQ(json["Id"], "BIOS");
EXPECT_EQ(json["Links"]["ActiveSoftwareImage"]["@odata.id"],
"/redfish/v1/UpdateService/FirmwareInventory/bios_active");
EXPECT_TRUE(json["Links"]["SoftwareImages"].is_array());
EXPECT_EQ(json["Links"]["SoftwareImages"].size(), 1);
EXPECT_EQ(json["Links"]["SoftwareImages"][0]["@odata.id"],
"/redfish/v1/UpdateService/FirmwareInventory/bios_active");
EXPECT_EQ(json["Links"]["SoftwareImages@odata.count"], 1);
EXPECT_EQ(json["Name"], "BIOS Configuration");
EXPECT_EQ(json["Oem"]["Google"]["MemTestBlob"], "");
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(
SnapshotFixture_Platform23,
// NOLINTNEXTLINE(whitespace/line_length): Test name is too long.
MultiHostPlatformHandleBiosServiceGetTestReturnsCorrectResponsInvalidBlobPath) {
std::string originalPath = biosSettingFilepathFromConfig;
biosSettingFilepathFromConfig = "/illegal/bios/setting/path";
absl::Cleanup bioSettingFilePathResetter = [originalPath]() {
biosSettingFilepathFromConfig = originalPath;
};
handleBiosServiceGet(app_, CreateRequest(), share_async_resp_, "system1");
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json["@odata.id"], "/redfish/v1/Systems/system1/Bios");
EXPECT_EQ(json["@odata.type"], "#Bios.v1_1_0.Bios");
EXPECT_EQ(json["Actions"]["#Bios.ResetBios"]["target"],
"/redfish/v1/Systems/system1/Bios/Actions/Bios.ResetBios");
EXPECT_EQ(json["Description"], "BIOS Configuration Service");
EXPECT_EQ(json["Id"], "BIOS");
EXPECT_EQ(json["Links"]["ActiveSoftwareImage"]["@odata.id"],
"/redfish/v1/UpdateService/FirmwareInventory/bios_active");
EXPECT_TRUE(json["Links"]["SoftwareImages"].is_array());
EXPECT_EQ(json["Links"]["SoftwareImages"].size(), 1);
EXPECT_EQ(json["Links"]["SoftwareImages"][0]["@odata.id"],
"/redfish/v1/UpdateService/FirmwareInventory/bios_active");
EXPECT_EQ(json["Links"]["SoftwareImages@odata.count"], 1);
EXPECT_EQ(json["Name"], "BIOS Configuration");
EXPECT_TRUE(json["Oem"]["Google"]["MemTestBlob"].is_null());
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST(HandleBios, handleBiosWithInvalidSystemId) {
auto share_async_resp = std::make_shared<bmcweb::AsyncResp>();
handleBios(share_async_resp, "system", true);
nlohmann::json& json = share_async_resp->res.jsonValue;
EXPECT_TRUE(json.find("Oem") == json.end());
}
class MultiHostBiosBlobPatchTest : public SnapshotFixture_Platform23 {
protected:
static void createEmptyBlobFiles() {
for (auto& biosSettingFilePath : biosSettingFilePathSet) {
std::filesystem::path path(biosSettingFilePath);
std::error_code ec;
std::filesystem::create_directories(path.parent_path(), ec);
if (ec) {
LOG(ERROR) << "Failed to create directory " << path.parent_path()
<< ": " << ec.message();
FAIL();
}
if (std::filesystem::exists(path)) {
// remove file if already exists
std::filesystem::remove(path, ec);
if (ec) {
LOG(ERROR) << "BIOS setting blob file " << path
<< " exists, failed to remove: " << ec.message();
FAIL();
}
}
// create emtpy file
std::ofstream out(path);
out.close();
}
}
static void SetUpTestSuite() {
SnapshotFixture_Platform23::SetUpTestSuite();
biosSettingFilepathFromConfig = biosSettingFilePathFromConfigOrignal;
}
static void TearDownTestSuite() {
SnapshotFixture_Platform23::TearDownTestSuite();
std::error_code ec;
// remove files
for (auto& biosSettingFilePath : biosSettingFilePathSet) {
std::filesystem::remove(biosSettingFilePath, ec);
if (ec) {
LOG(WARNING) << "Failed to remove BIOS setting blob file "
<< biosSettingFilePath
<< " in TearDownTestSuite():" << ec.message();
ec.clear();
}
}
// remove directories
std::filesystem::remove_all("/tmp/soc_firmware/", ec);
if (ec) {
LOG(WARNING) << "Failed to remove test directory /tmp/soc_firmware/ in "
"TearDownTestSuite():"
<< ec.message();
}
}
void TearDown() override {
for (auto& biosSettingFilePath : biosSettingFilePathSet) {
if (std::filesystem::exists(biosSettingFilePath)) {
// remove file if exists
std::error_code ec;
std::filesystem::remove(biosSettingFilePath, ec);
if (ec) {
LOG(ERROR) << "BIOS setting blob file " << biosSettingFilePath
<< " exists, but failed to remove: " << ec.message();
}
}
}
// reset filepath
biosSettingFilepathFromConfig = biosSettingFilePathFromConfigOrignal;
}
static inline const std::string biosSettingFilePathFromConfigOrignal =
"/tmp/soc_firmware/host/vmtppr.cfg";
static inline const std::vector<std::string> biosSettingFilePathSet = {
"/tmp/soc_firmware/host0/vmtppr.cfg",
"/tmp/soc_firmware/host1/vmtppr.cfg",
};
};
TEST_F(MultiHostBiosBlobPatchTest, PatchBiosSettingsToNonExistSystem) {
// no BIOS setting file if createEmptyBlobFiles() is not called
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"AAEC\"}}}");
// peer is authenticated
req.peer_authenticated = true;
patchBiosSettings(app_, req, share_async_resp_, "system");
RunIoUntilDone();
// Now that CreateRequest sets Content-Type, it should proceed to business
// logic and return not_found for non-existent system in multi-host mode.
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::not_found);
}
TEST_F(MultiHostBiosBlobPatchTest, PatchBiosSettingsInvalidFilePath) {
// no BIOS setting file if createEmptyBlobFiles() is not called
crow::Logger::setLogLevel(crow::LogLevel::Error);
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"AAEC\"}}}");
// peer is authenticated
req.peer_authenticated = true;
// set an invalid BIOS setting path for multihost platform.
biosSettingFilepathFromConfig = "run/soc_firmware/whatever/vmtppr.cfg";
patchBiosSettings(app_, req, share_async_resp_, "system1");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
TEST_F(MultiHostBiosBlobPatchTest, PatchBiosSettingsFailedWithPldmFailure) {
crow::Logger::setLogLevel(crow::LogLevel::Error);
// creates empty testing blob file
createEmptyBlobFiles();
// Simulate a Failed DBus Call
// Setup dbus mocking
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
_, An<absl::AnyInvocable<void(const boost::system::error_code&)>&&>(),
std::string(kPldmServiceName) + "0", std::string(kPldmObjectPath),
std::string(kPldmControlInterface),
std::string(kPldmLoadSingleFileMethod),
static_cast<uint16_t>(BIOS_SETTING_FILE_HANDLE)))
.Times(1)
.WillOnce(SimulateFailedAsyncPostDbusCallThreadSafeAction::
SimulateFailedAsyncPostDbusCall());
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"AAEC\"}}}");
// peer is authenticated
req.peer_authenticated = true;
patchBiosSettings(app_, req, share_async_resp_, "system1");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
TEST_F(MultiHostBiosBlobPatchTest, PatchBiosSettingsSuccessfully) {
// creates empty testing blob file
createEmptyBlobFiles();
// Ensure directories exist for both hosts (for reading/writing)
std::error_code ec;
std::filesystem::create_directories("/tmp/soc_firmware/host0", ec);
std::filesystem::create_directories("/tmp/soc_firmware/host1", ec);
// Simulate a successful DBus Call
// Setup dbus mocking
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
_, An<absl::AnyInvocable<void(const boost::system::error_code&)>&&>(),
std::string(kPldmServiceName) + "0", std::string(kPldmObjectPath),
std::string(kPldmControlInterface),
std::string(kPldmLoadSingleFileMethod),
static_cast<uint16_t>(BIOS_SETTING_FILE_HANDLE)))
.Times(1)
.WillOnce(SimulateSuccessfulAsyncPostDbusCallThreadSafeAction::
SimulateSuccessfulAsyncPostDbusCall());
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"AAEC\"}}}");
// peer is authenticated
req.peer_authenticated = true;
// patch to system1
patchBiosSettings(app_, req, share_async_resp_, "system1");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::no_content);
share_async_resp_->res.clear();
handleBiosServiceGet(app_, CreateRequest(), share_async_resp_, "system2");
RunIoUntilDone();
// ensure the patch to system1 does not affect system2.
EXPECT_EQ(share_async_resp_->res.jsonValue["Oem"]["Google"]["MemTestBlob"],
"");
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
} // namespace
} // namespace redfish