blob: 84377aafc6d98974679f71cab03bbc13be19abdc [file] [log] [blame]
#include "absl/cleanup/cleanup.h"
#include "absl/log/log.h"
#include "bmcweb_config.h"
#include "app.hpp"
#include "async_resp.hpp"
#include "bios.hpp"
#include "http_response.hpp"
#include "nlohmann/json.hpp"
#include "snapshot_fixture.hpp"
#include <boost/system/error_code.hpp>
#include <filesystem>
#include <fstream>
#include <memory>
#include <optional>
#include <vector>
#include <gmock/gmock.h> // IWYU pragma: keep
#include <gtest/gtest.h> // IWYU pragma: keep
// 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::KeyType;
using ::managedStore::ManagedObjectStoreContext;
using ::managedStore::ManagedType;
using ::managedStore::SimulateFailedAsyncPostDbusCallThreadSafeAction;
using ::managedStore::SimulateSuccessfulAsyncPostDbusCallThreadSafeAction;
using ::managedStore::ValueType;
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,
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,
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);
if (std::filesystem::exists(path))
{
// remove file if already exists
std::error_code ec;
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::error_code ec;
std::filesystem::create_directories(path.parent_path());
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("/run/soc_firmware/", ec);
if (ec)
{
LOG(WARNING)
<< "Failed to remove test directory /run/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 =
"/run/soc_firmware/host/vmtppr.cfg";
static inline const std::vector<std::string> biosSettingFilePathSet = {
"/run/soc_firmware/host0/vmtppr.cfg",
"/run/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();
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(
*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)
{
crow::Logger::setLogLevel(crow::LogLevel::Error);
// creates empty testing blob file
createEmptyBlobFiles();
// Simulate a successful DBus Call
// Setup dbus mocking
EXPECT_CALL(
*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