| #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 |