| #include "log_services.hpp" | |
| #include <array> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <cstdio> | |
| #include <filesystem> // NOLINT | |
| #include <fstream> | |
| #include <functional> | |
| #include <iostream> | |
| #include <map> | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include <string_view> | |
| #include <tuple> | |
| #include <unordered_map> | |
| #include <utility> | |
| #include <vector> | |
| #include <gmock/gmock.h> | |
| #include <gtest/gtest.h> | |
| #include "absl/functional/any_invocable.h" | |
| #include "app.hpp" | |
| #include "http_request.hpp" | |
| #include "utility.hpp" | |
| #include "async_resp.hpp" | |
| #include "dbus_utility.hpp" | |
| #include "query.hpp" | |
| #include "registries/privilege_registry.hpp" | |
| #include "health.hpp" | |
| #include "test/redfish-core/lib/snapshot_fixture.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/native_types.hpp" | |
| namespace redfish { | |
| namespace { | |
| using ::dbus::utility::DBusInteracesMap; | |
| using ::dbus::utility::DBusPropertiesMap; | |
| using ::dbus::utility::DbusVariantType; | |
| using ::dbus::utility::ManagedObjectType; | |
| using ::dbus::utility::MapperGetSubTreePathsResponse; | |
| using ::managedStore::KeyType; | |
| using ::managedStore::ManagedType; | |
| using ::managedStore::SimulateFailedAsyncPostDbusCallThreadSafeAction; | |
| using ::managedStore:: | |
| SimulateFailedAsyncPostDbusCallThreadSafeWithEmptyValueAction; | |
| using ::managedStore:: | |
| SimulateSuccessfulAsyncPostDbusCallThreadSafeWithValueAction; | |
| using ::managedStore::ValueType; | |
| using ::testing::_; | |
| using ::testing::An; | |
| using PostPackageRepairStatusParam = std::vector< | |
| std::tuple<uint16_t, uint16_t, uint16_t, uint16_t, std::vector<uint16_t>>>; | |
| const uint64_t BMC_DUMP_SIZE = 10; | |
| constexpr std::array<std::string_view, 1> kSystemInterfaces = { | |
| "xyz.openbmc_project.Inventory.Item.System"}; | |
| TEST(LogServicesDumpServiceTest, AllDumpTypesAreReturnedAsCorrectStrings) { | |
| std::map<DumpType, std::string_view> dumpTypeMap = { | |
| {DumpType::BMC_DUMP, "BMC"}, | |
| {DumpType::SYSTEM_DUMP, "System"}, | |
| {DumpType::BMC_FAULT_LOG, "FaultLog"}, | |
| {DumpType::SYSTEM_FAULT_LOG, "FaultLog"}, | |
| }; | |
| for (size_t dumpIndex = 0; dumpIndex < dumpTypeMap.size(); ++dumpIndex) { | |
| DumpType dumpType = static_cast<DumpType>(dumpIndex); | |
| EXPECT_EQ(dumpTypeToString(dumpType), dumpTypeMap[dumpType]); | |
| } | |
| } | |
| std::shared_ptr<ValueType> CreateMockFaultLogEntry() { | |
| ManagedObjectType mockFaultLogEntry{{std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/0/entry/1"), | |
| DBusInteracesMap{ | |
| {// First Mock Completed Status | |
| std::make_pair("xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair("Type", | |
| "xyz.openbmc_project.Common.FaultLogType." | |
| "FaultLogTypes.CPER"), | |
| std::make_pair("PrimaryLogId", "1")}})}})}}; | |
| return managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry)); | |
| } | |
| TEST_F(SnapshotFixture, LogServiceFaultLogEntriesGetSuccessfulResponse) { | |
| std::string systemId = "system1"; | |
| // Looking for Key: | |
| // kManagedObject|xyz.openbmc_project.Dump.Manager|/xyz/openbmc_project/dump | |
| KeyType key(ManagedType::kManagedObject, "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore(key, CreateMockFaultLogEntry()) | |
| .ok()); | |
| handleLogServiceFaultLogEntriesGet(app_, CreateRequest(), share_async_resp_, | |
| systemId); | |
| RunIoUntilDone(); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["@odata.id"], "/redfish/v1/Systems/" + systemId + | |
| "/LogServices/FaultLog/Entries"); | |
| EXPECT_EQ(json["@odata.type"], "#LogEntryCollection.LogEntryCollection"); | |
| EXPECT_EQ(json["Description"], "Collection of FaultLog Dump Entries"); | |
| EXPECT_EQ(json["Name"], "FaultLog Dump Entries"); | |
| EXPECT_EQ(json["Members"].size(), json["Members@odata.count"]); | |
| // Hardcoding the verification of the Members check | |
| nlohmann::json& faultLog1 = json["Members"][0]; | |
| EXPECT_EQ(faultLog1["@odata.id"], | |
| "/redfish/v1/Systems/system1/LogServices/FaultLog/Entries/1"); | |
| EXPECT_EQ(faultLog1["@odata.type"], "#LogEntry.v1_11_0.LogEntry"); | |
| EXPECT_EQ(faultLog1["AdditionalDataURI"], | |
| "/redfish/v1/Systems/system1/LogServices/HostCper/Entries/1"); | |
| EXPECT_EQ(faultLog1["Created"], "1970-01-01T00:00:00.000000+00:00"); | |
| EXPECT_EQ(faultLog1["DiagnosticDataType"], "OEM"); | |
| EXPECT_EQ(faultLog1["EntryType"], "Oem"); | |
| EXPECT_EQ(faultLog1["Id"], "1"); | |
| EXPECT_EQ(faultLog1["Links"]["RelatedLogEntries"].size(), 1); | |
| EXPECT_EQ(faultLog1["Links"]["RelatedLogEntries"][0]["@odata.id"], | |
| "/redfish/v1/Systems/system1/LogServices/HostCper/Entries/1"); | |
| EXPECT_EQ(faultLog1["Name"], "FaultLog Dump Entry"); | |
| EXPECT_EQ(faultLog1["OEMDiagnosticDataType"], "OpenBMC Fault Log"); | |
| EXPECT_EQ(faultLog1["OemRecordFormat"], "CPER"); | |
| // Response validation | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| } | |
| nlohmann::json createLogEntry(const std::string& systemId, | |
| const std::string& entryId, | |
| const std::string& hostCperEntryUri) { | |
| nlohmann::json entry; | |
| entry["@odata.id"] = "/redfish/v1/Systems/" + systemId + | |
| "/LogServices/FaultLog/Entries/" + entryId; | |
| entry["@odata.type"] = "#LogEntry.v1_11_0.LogEntry"; | |
| entry["AdditionalDataURI"] = hostCperEntryUri; | |
| entry["Created"] = "2024-08-26T20:21:09.735414+00:00"; | |
| entry["DiagnosticDataType"] = "OEM"; | |
| entry["EntryType"] = "Oem"; | |
| entry["Id"] = entryId; | |
| nlohmann::json hostCperEntry; | |
| hostCperEntry["@odata.id"] = hostCperEntryUri; | |
| entry["Links"]["RelatedLogEntries"][0] = hostCperEntry; | |
| return entry; | |
| } | |
| TEST_F(SnapshotFixture, QueryFaultCollectionFilterIncomplete) { | |
| std::string systemId = "system1"; | |
| const std::string collectionUri = | |
| "/redfish/v1/Systems/" + systemId + "/LogServices/HostCper/Entries"; | |
| const std::string hostCperEntryUri = collectionUri + "/1"; | |
| std::string_view fileContent = "Q1BFUgEB/////wEAAgAAA"; | |
| BMCWEB_ROUTE(app_, "/redfish/v1/Systems/<str>/LogServices/HostCper/Entries") | |
| .privileges(redfish::privileges::getLogServiceCollection) | |
| .methods(boost::beast::http::verb::get)( | |
| [this, &fileContent]( | |
| const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string systemId) { | |
| if (!redfish::setUpRedfishRoute(app_, req, asyncResp)) { | |
| return; | |
| } | |
| nlohmann::json& thisEntry = asyncResp->res.jsonValue; | |
| thisEntry["@odata.id"] = "/redfish/v1/Systems/" + systemId + | |
| "/LogServices/HostCper/Entries"; | |
| thisEntry["@odata.type"] = "#LogEntryCollection.LogEntryCollection"; | |
| thisEntry["Description"] = "Collection of Host CPER Entries"; | |
| thisEntry["Members"] = nlohmann::json::array(); | |
| thisEntry["Members@odata.count"] = 1; | |
| thisEntry["Name"] = "Host CPER Entries"; | |
| nlohmann::json hostCperEntry; | |
| hostCperEntry["@odata.id"] = "/redfish/v1/Systems/" + systemId + | |
| "/LogServices/HostCper/Entries/1"; | |
| hostCperEntry["@odata.type"] = "#LogEntry.v1_11_0.LogEntry"; | |
| hostCperEntry["Created"] = "2024-08-26T20:21:09.735414+00:00"; | |
| hostCperEntry["DiagnosticData"] = fileContent; | |
| hostCperEntry["DiagnosticDataType"] = "CPER"; | |
| hostCperEntry["EntryType"] = "Oem"; | |
| hostCperEntry["Id"] = "1"; | |
| hostCperEntry["Name"] = "Host CPER Entry"; | |
| thisEntry["Members"].push_back(std::move(hostCperEntry)); | |
| }); | |
| app_.validate(); | |
| app_.run(); | |
| nlohmann::json originalMembers = nlohmann::json::array(); | |
| std::unordered_map<std::string, nlohmann::json::json_pointer> faultLogEntries; | |
| nlohmann::json::json_pointer path1 = | |
| ""_json_pointer / 0 / "Links" / "RelatedLogEntries" / 0; | |
| faultLogEntries.emplace(hostCperEntryUri, path1); | |
| originalMembers.push_back(createLogEntry(systemId, "1", hostCperEntryUri)); | |
| nlohmann::json::json_pointer path2 = | |
| ""_json_pointer / 1 / "Links" / "RelatedLogEntries" / 1; | |
| faultLogEntries.emplace(hostCperEntryUri, path2); | |
| originalMembers.push_back(createLogEntry(systemId, "2", hostCperEntryUri)); | |
| redfish::queryFaultCollection(app_, CreateRequest(), share_async_resp_, | |
| collectionUri, faultLogEntries, | |
| originalMembers); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["Members"].size(), 1); | |
| EXPECT_EQ( | |
| share_async_resp_->res.jsonValue["Members"][path1 / "DiagnosticData"], | |
| fileContent); | |
| } | |
| void commonChecksForBootLogService( | |
| const std::shared_ptr<bmcweb::AsyncResp>& share_async_resp, | |
| const std::string& serviceODataId) { | |
| nlohmann::json& json = share_async_resp->res.jsonValue; | |
| EXPECT_EQ(json["@odata.type"], "#LogService.v1_1_0.LogService"); | |
| EXPECT_EQ(json["Name"], "Boot Time Log Service"); | |
| EXPECT_EQ(json["Description"], "Boot Time Log Service"); | |
| EXPECT_EQ(json["Id"], "BootTime"); | |
| EXPECT_EQ(json["OverWritePolicy"], "WrapsWhenFull"); | |
| EXPECT_EQ(json["@odata.id"], serviceODataId); | |
| EXPECT_EQ(json["LogEntryType"], "Multiple"); | |
| EXPECT_TRUE(json.contains("Entries")); | |
| EXPECT_EQ(json["Entries"]["@odata.id"], serviceODataId + "/Entries"); | |
| // Response validation | |
| EXPECT_EQ(share_async_resp->res.result(), boost::beast::http::status::ok); | |
| } | |
| TEST_F(SnapshotFixture, GetSystemBootLogServicesMultihost) { | |
| std::string systemId = "system1"; | |
| handleSystemBootTimeLogServiceGet(app_, CreateRequest(), share_async_resp_, | |
| systemId); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::not_found); | |
| } | |
| TEST_F(SnapshotFixture, GetSystemBootLogServices) { | |
| std::string systemId = "system"; | |
| handleSystemBootTimeLogServiceGet(app_, CreateRequest(), share_async_resp_, | |
| systemId); | |
| RunIoUntilDone(); | |
| std::string serviceODataId = | |
| "/redfish/v1/Systems/" + systemId + "/LogServices/BootTime"; | |
| commonChecksForBootLogService(share_async_resp_, serviceODataId); | |
| } | |
| TEST_F(SnapshotFixture, GetManagerLogServices) { | |
| handleManagerBootTimeLogServiceGet(app_, CreateRequest(), share_async_resp_); | |
| RunIoUntilDone(); | |
| std::string serviceODataId = "/redfish/v1/Managers/bmc/LogServices/BootTime"; | |
| commonChecksForBootLogService(share_async_resp_, serviceODataId); | |
| } | |
| TEST_F(SnapshotFixture, GetSystemBootLogServiceEntriesMultihost) { | |
| std::string systemId = "system1"; | |
| handleSystemBootTimeLogsEntryCollectionGet(app_, CreateRequest(), | |
| share_async_resp_, systemId); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::not_found); | |
| } | |
| void setCheckpointsInBootLogService( | |
| const std::string& host, const std::vector<BootTimeCheckpoint> checkpoints, | |
| bool dbusException) { | |
| if (dbusException) { | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const std::vector<BootTimeCheckpoint>&)>&&>(), | |
| "com.google.gbmc.boot_time_monitor", | |
| "/xyz/openbmc_project/time/boot/" + host, | |
| "xyz.openbmc_project.Time.Boot.Checkpoint", "GetCheckpointList")) | |
| .Times(testing::AtMost(1)) | |
| .WillOnce( | |
| SimulateFailedAsyncPostDbusCallThreadSafeWithEmptyValueAction:: | |
| SimulateFailedAsyncPostDbusCallWithEmptyValue()); | |
| } else { | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const std::vector<BootTimeCheckpoint>&)>&&>(), | |
| "com.google.gbmc.boot_time_monitor", | |
| "/xyz/openbmc_project/time/boot/" + host, | |
| "xyz.openbmc_project.Time.Boot.Checkpoint", "GetCheckpointList")) | |
| .Times(testing::AtMost(1)) | |
| .WillOnce(SimulateSuccessfulAsyncPostDbusCallThreadSafeWithValueAction< | |
| std::vector<BootTimeCheckpoint>>:: | |
| SimulateSuccessfulAsyncPostDbusCallWithValue( | |
| std::make_shared<std::vector<BootTimeCheckpoint>>( | |
| checkpoints))); | |
| } | |
| } | |
| void setDurationsInBootLogService( | |
| const std::string& host, const std::vector<BootTimeDuration>& durations, | |
| bool dbusException) { | |
| if (dbusException) { | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const std::vector<BootTimeDuration>&)>&&>(), | |
| "com.google.gbmc.boot_time_monitor", | |
| "/xyz/openbmc_project/time/boot/" + host, | |
| "xyz.openbmc_project.Time.Boot.Duration", "GetAdditionalDurations")) | |
| .Times(testing::AtMost(1)) | |
| .WillOnce( | |
| SimulateFailedAsyncPostDbusCallThreadSafeWithEmptyValueAction:: | |
| SimulateFailedAsyncPostDbusCallWithEmptyValue()); | |
| } else { | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const std::vector<BootTimeDuration>&)>&&>(), | |
| "com.google.gbmc.boot_time_monitor", | |
| "/xyz/openbmc_project/time/boot/" + host, | |
| "xyz.openbmc_project.Time.Boot.Duration", "GetAdditionalDurations")) | |
| .Times(testing::AtMost(1)) | |
| .WillOnce(SimulateSuccessfulAsyncPostDbusCallThreadSafeWithValueAction< | |
| std::vector<BootTimeDuration>>:: | |
| SimulateSuccessfulAsyncPostDbusCallWithValue( | |
| std::make_shared<std::vector<BootTimeDuration>>( | |
| durations))); | |
| } | |
| } | |
| void setIsRebootingInBootLogService(const std::string& host, | |
| const bool& isRebooting, | |
| bool dbusException) { | |
| if (!dbusException) { | |
| KeyType key(ManagedType::kManagedProperty, | |
| "com.google.gbmc.boot_time_monitor", | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/time/boot/" + host), | |
| "xyz.openbmc_project.Time.Boot.Statistic", "IsRebooting"); | |
| std::shared_ptr<ValueType> mockIsRebooting = | |
| managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(isRebooting)); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore(key, mockIsRebooting) | |
| .ok()); | |
| } else { | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void(const boost::system::error_code&)>&&>(), | |
| "com.google.gbmc.boot_time_monitor", | |
| "/xyz/openbmc_project/time/boot/" + host, | |
| "org.freedesktop.DBus.Properties", "Get", | |
| "xyz.openbmc_project.Time.Boot.Statistic", "IsRebooting", | |
| dbus::utility::DbusVariantType(true))) | |
| .Times(testing::AtMost(1)) | |
| .WillOnce(SimulateFailedAsyncPostDbusCallThreadSafeAction:: | |
| SimulateFailedAsyncPostDbusCall()); | |
| } | |
| } | |
| void validateBootTimeCheckpoint( | |
| const nlohmann::json& diagnosticData, const nlohmann::json& logEntry, | |
| const std::optional<size_t>& specificIndex, | |
| const std::string& parentODataId, | |
| const std::vector<BootTimeCheckpoint>& checkpoints) { | |
| bool found = false; | |
| EXPECT_EQ(diagnosticData.size(), 3); | |
| EXPECT_TRUE(diagnosticData.contains("Name")); | |
| EXPECT_TRUE(diagnosticData.contains("WallTimeMs")); | |
| EXPECT_TRUE(diagnosticData.contains("MonoTimeMs")); | |
| size_t currentIndex = specificIndex.value_or(0); | |
| for (auto checkpoint : checkpoints) { | |
| const auto [name, wallTime, monoTime] = checkpoint; | |
| if (diagnosticData["Name"] == name && | |
| diagnosticData["WallTimeMs"] == wallTime && | |
| diagnosticData["MonoTimeMs"] == monoTime) { | |
| found = true; | |
| const std::string id = "Checkpoint" + std::to_string(currentIndex); | |
| EXPECT_EQ(logEntry.at("Id").get<std::string>(), id); | |
| EXPECT_EQ(logEntry.at("@odata.id").get<std::string>(), | |
| parentODataId + "/" + id); | |
| break; | |
| } | |
| currentIndex++; | |
| } | |
| EXPECT_TRUE(found); | |
| } | |
| void validateBootTimeDuration(const nlohmann::json& diagnosticData, | |
| const nlohmann::json& logEntry, | |
| const std::optional<size_t>& specificIndex, | |
| const std::string& parentODataId, | |
| const std::vector<BootTimeDuration>& durations) { | |
| bool found = false; | |
| EXPECT_EQ(diagnosticData.size(), 2); | |
| EXPECT_TRUE(diagnosticData.contains("Name")); | |
| EXPECT_TRUE(diagnosticData.contains("DurationMs")); | |
| size_t currentIndex = specificIndex.value_or(0); | |
| for (auto duration : durations) { | |
| const auto [name, durationMs] = duration; | |
| if (diagnosticData["Name"] == name && | |
| diagnosticData["DurationMs"] == durationMs) { | |
| found = true; | |
| const std::string id = "Duration" + std::to_string(currentIndex); | |
| EXPECT_EQ(logEntry.at("Id").get<std::string>(), id); | |
| EXPECT_EQ(logEntry.at("@odata.id").get<std::string>(), | |
| parentODataId + "/" + id); | |
| break; | |
| } | |
| currentIndex++; | |
| } | |
| EXPECT_TRUE(found); | |
| } | |
| void validateBootTimeStatistic(const nlohmann::json& diagnosticData, | |
| const nlohmann::json& logEntry, | |
| const std::string& parentODataId, | |
| const bool& isRebooting) { | |
| EXPECT_EQ(diagnosticData.size(), 1); | |
| EXPECT_TRUE(diagnosticData.contains("IsRebooting")); | |
| EXPECT_EQ(diagnosticData["IsRebooting"], isRebooting); | |
| const std::string id = "Statistic0"; | |
| EXPECT_EQ(logEntry.at("Id").get<std::string>(), id); | |
| EXPECT_EQ(logEntry.at("@odata.id").get<std::string>(), | |
| parentODataId + "/" + id); | |
| } | |
| void checkLogEntries(const nlohmann::json& logEntries, | |
| const std::vector<BootTimeCheckpoint>& checkpoints, | |
| const std::vector<BootTimeDuration>& durations, | |
| const std::optional<bool> isRebooting, | |
| const std::string& parentODataId, | |
| const std::optional<size_t> specificIndex) { | |
| for (const nlohmann::json& logEntry : logEntries) { | |
| EXPECT_EQ(logEntry["@odata.type"], "#LogEntry.v1_15_0.LogEntry"); | |
| EXPECT_EQ(logEntry["Name"], "Boot Time Log Entry"); | |
| EXPECT_EQ(logEntry["EntryType"], "Oem"); | |
| EXPECT_TRUE(logEntry.contains("OEMDiagnosticDataType")); | |
| EXPECT_TRUE(logEntry.contains("DiagnosticData")); | |
| std::string encodedData = logEntry["DiagnosticData"]; | |
| EXPECT_GT(encodedData.size(), 0); | |
| nlohmann::json diagnosticData; | |
| std::string diagnosticDataStr; | |
| EXPECT_TRUE(crow::utility::base64Decode(encodedData, diagnosticDataStr)) | |
| << "Decoding failed for the DiagnosticData"; | |
| EXPECT_EQ(logEntry["Message"], diagnosticDataStr); | |
| try { | |
| diagnosticData = nlohmann::json::parse(diagnosticDataStr); | |
| } catch (nlohmann::json::parse_error& e) { | |
| ADD_FAILURE() << "Could not parse diagnostic data. " << e.what(); | |
| } | |
| std::string type = logEntry["OEMDiagnosticDataType"]; | |
| if (type == "Checkpoint") { | |
| validateBootTimeCheckpoint(diagnosticData, logEntry, specificIndex, | |
| parentODataId, checkpoints); | |
| } else if (type == "Duration") { | |
| validateBootTimeDuration(diagnosticData, logEntry, specificIndex, | |
| parentODataId, durations); | |
| } else if (type == "Statistic") { | |
| validateBootTimeStatistic(diagnosticData, logEntry, parentODataId, | |
| isRebooting.value_or(false)); | |
| } else { | |
| ADD_FAILURE() << "Unexpected OEMDiagnosticDataType found: " << type; | |
| } | |
| } | |
| } | |
| TEST_F(SnapshotFixture, GetSystemBootLogServiceEntries) { | |
| std::string systemId = "system"; | |
| std::vector<BootTimeCheckpoint> checkpoints; | |
| checkpoints.push_back(BootTimeCheckpoint({"dummyState", 100, 200})); | |
| setCheckpointsInBootLogService("host0", checkpoints, false); | |
| std::vector<BootTimeDuration> durations; | |
| durations.push_back(BootTimeDuration({"dummyDuration", 300})); | |
| setDurationsInBootLogService("host0", durations, false); | |
| setIsRebootingInBootLogService("host0", true, false); | |
| // call target method | |
| handleSystemBootTimeLogsEntryCollectionGet(app_, CreateRequest(), | |
| share_async_resp_, systemId); | |
| // Perform dbus calls | |
| RunIoUntilDone(); | |
| const std::string odataId = | |
| "/redfish/v1/Systems/" + systemId + "/LogServices/BootTime/Entries"; | |
| // expect starts | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["@odata.id"], odataId); | |
| checkLogEntries(share_async_resp_->res.jsonValue["Members"], checkpoints, | |
| durations, true, odataId, std::nullopt); | |
| size_t expected_entries = checkpoints.size() + durations.size() + 1; | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["Members@odata.count"], | |
| expected_entries); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| } | |
| TEST_F(SnapshotFixture, GetManagerBootLogServiceEntries) { | |
| std::vector<BootTimeCheckpoint> checkpoints; | |
| checkpoints.push_back(BootTimeCheckpoint({"dummyState", 100, 200})); | |
| setCheckpointsInBootLogService("bmc", checkpoints, false); | |
| std::vector<BootTimeDuration> durations; | |
| durations.push_back(BootTimeDuration({"dummyDuration", 300})); | |
| setDurationsInBootLogService("bmc", durations, false); | |
| setIsRebootingInBootLogService("bmc", true, false); | |
| // call target method | |
| handleManagerBootTimeLogsEntryCollectionGet(app_, CreateRequest(), | |
| share_async_resp_); | |
| // Perform dbus calls | |
| RunIoUntilDone(); | |
| // expect starts | |
| const std::string odataId = | |
| "/redfish/v1/Managers/bmc/LogServices/BootTime/Entries"; | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["@odata.id"], odataId); | |
| checkLogEntries(share_async_resp_->res.jsonValue["Members"], checkpoints, | |
| durations, true, odataId, std::nullopt); | |
| size_t expected_entries = checkpoints.size() + durations.size() + 1; | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["Members@odata.count"], | |
| expected_entries); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| } | |
| struct BootLogEntryTestCase { | |
| BootTimeDataType type; | |
| std::vector<BootTimeCheckpoint> checkpoints; | |
| std::vector<BootTimeDuration> durations; | |
| std::optional<bool> rebooting; | |
| std::string entryId; | |
| bool dbusException; | |
| size_t expectedIndex; | |
| bool expectErr; | |
| }; | |
| void testBootLogEntry(App& app, const crow::Request& req, | |
| std::shared_ptr<bmcweb::AsyncResp>& share_async_resp, | |
| const std::string& systemId, const std::string& host, | |
| const std::string odataId, const bool& testSystem, | |
| const std::function<void()>& RunIoUntilDone) { | |
| std::vector<BootTimeCheckpoint> defaultCheckpoints; | |
| defaultCheckpoints.push_back(BootTimeCheckpoint({"dummyState1", 100, 200})); | |
| defaultCheckpoints.push_back(BootTimeCheckpoint({"dummyState2", 300, 400})); | |
| std::vector<BootTimeDuration> defaultDurations; | |
| defaultDurations.push_back(BootTimeDuration({"dummyDuration1", 300})); | |
| defaultDurations.push_back(BootTimeDuration({"dummyDuration2", 600})); | |
| std::vector<BootLogEntryTestCase> testCases = { | |
| // successful response | |
| {BootTimeDataType::CHECKPOINT, | |
| defaultCheckpoints, | |
| {}, | |
| std::nullopt, | |
| "Checkpoint0", | |
| false, | |
| 0, | |
| false}, | |
| {BootTimeDataType::CHECKPOINT, | |
| defaultCheckpoints, | |
| {}, | |
| std::nullopt, | |
| "Checkpoint1", | |
| false, | |
| 1, | |
| false}, | |
| {BootTimeDataType::DURATION, | |
| {}, | |
| defaultDurations, | |
| std::nullopt, | |
| "Duration0", | |
| false, | |
| 0, | |
| false}, | |
| {BootTimeDataType::DURATION, | |
| {}, | |
| defaultDurations, | |
| std::nullopt, | |
| "Duration1", | |
| false, | |
| 1, | |
| false}, | |
| {BootTimeDataType::STATISTIC, | |
| {}, | |
| {}, | |
| true, | |
| "Statistic0", | |
| false, | |
| 0, | |
| false}, // 5 | |
| // error response | |
| {BootTimeDataType::CHECKPOINT, | |
| {}, | |
| {}, | |
| std::nullopt, | |
| "Checkpoint0", | |
| false, | |
| 0, | |
| true}, | |
| {BootTimeDataType::CHECKPOINT, | |
| defaultCheckpoints, | |
| {}, | |
| std::nullopt, | |
| "Checkpoint2", | |
| false, | |
| 0, | |
| true}, | |
| {BootTimeDataType::DURATION, | |
| {}, | |
| {}, | |
| std::nullopt, | |
| "Duration0", | |
| false, | |
| 0, | |
| true}, | |
| {BootTimeDataType::DURATION, | |
| {}, | |
| defaultDurations, | |
| std::nullopt, | |
| "Duration2", | |
| false, | |
| 0, | |
| true}, | |
| {BootTimeDataType::STATISTIC, | |
| {}, | |
| {}, | |
| true, | |
| "Statistic1", | |
| false, | |
| 0, | |
| true}, // 10 | |
| {BootTimeDataType::STATISTIC, | |
| {}, | |
| {}, | |
| std::nullopt, | |
| "Statistic0", | |
| false, | |
| 0, | |
| true}, | |
| {BootTimeDataType::CHECKPOINT, | |
| defaultCheckpoints, | |
| {}, | |
| std::nullopt, | |
| "", | |
| false, | |
| 0, | |
| true}, | |
| {BootTimeDataType::CHECKPOINT, | |
| defaultCheckpoints, | |
| {}, | |
| std::nullopt, | |
| "abc", | |
| false, | |
| 0, | |
| true}, | |
| {BootTimeDataType::CHECKPOINT, | |
| defaultCheckpoints, | |
| {}, | |
| std::nullopt, | |
| "-", | |
| false, | |
| 0, | |
| true}, | |
| {BootTimeDataType::CHECKPOINT, | |
| defaultCheckpoints, | |
| {}, | |
| std::nullopt, | |
| "Checkpoint", | |
| false, | |
| 0, | |
| true}, // 15 | |
| {BootTimeDataType::CHECKPOINT, | |
| defaultCheckpoints, | |
| {}, | |
| std::nullopt, | |
| "Checkpoint-1", | |
| false, | |
| 0, | |
| true}, | |
| {BootTimeDataType::CHECKPOINT, | |
| defaultCheckpoints, | |
| {}, | |
| std::nullopt, | |
| "-1", | |
| false, | |
| 0, | |
| true}, | |
| {BootTimeDataType::CHECKPOINT, | |
| defaultCheckpoints, | |
| {}, | |
| std::nullopt, | |
| "CheckpointABC", | |
| false, | |
| 0, | |
| true}, | |
| {BootTimeDataType::CHECKPOINT, | |
| defaultCheckpoints, | |
| {}, | |
| std::nullopt, | |
| "Checkpoint0", | |
| true, | |
| 0, | |
| true}, | |
| {BootTimeDataType::DURATION, | |
| {}, | |
| defaultDurations, | |
| std::nullopt, | |
| "Duration0", | |
| true, | |
| 0, | |
| true}, // 20 | |
| {BootTimeDataType::STATISTIC, | |
| {}, | |
| {}, | |
| true, | |
| "Statistic0", | |
| true, | |
| 0, | |
| true}, // 21 | |
| }; | |
| size_t testNo = 1; | |
| for (auto testCase : testCases) { | |
| std::cout << "Test Case#" << testNo++ << '\n'; | |
| // Setup | |
| if (testCase.type == BootTimeDataType::CHECKPOINT) { | |
| setCheckpointsInBootLogService(host, testCase.checkpoints, | |
| testCase.dbusException); | |
| } else if (testCase.type == BootTimeDataType::DURATION) { | |
| setDurationsInBootLogService(host, testCase.durations, | |
| testCase.dbusException); | |
| } else if (testCase.rebooting.has_value()) { | |
| setIsRebootingInBootLogService(host, testCase.rebooting.value(), | |
| testCase.dbusException); | |
| } | |
| // Call Target Method | |
| if (testSystem) { | |
| handleSystemBootTimeLogsEntryGet(app, req, share_async_resp, systemId, | |
| testCase.entryId); | |
| } else { | |
| handleManagerBootTimeLogsEntryGet(app, req, share_async_resp, | |
| testCase.entryId); | |
| } | |
| // Perform dbus calls | |
| RunIoUntilDone(); | |
| // Match expectations | |
| if (testCase.expectErr) { | |
| EXPECT_TRUE(share_async_resp->res.jsonValue.contains("error")); | |
| continue; | |
| } else { | |
| EXPECT_EQ(share_async_resp->res.result(), boost::beast::http::status::ok); | |
| } | |
| nlohmann::json logEntryArray = nlohmann::json::array(); | |
| logEntryArray.push_back(share_async_resp->res.jsonValue); | |
| if (testCase.type == BootTimeDataType::CHECKPOINT) { | |
| checkLogEntries(logEntryArray, | |
| {testCase.checkpoints[testCase.expectedIndex]}, {}, | |
| std::nullopt, odataId, testCase.expectedIndex); | |
| } else if (testCase.type == BootTimeDataType::DURATION) { | |
| checkLogEntries(logEntryArray, {}, | |
| {testCase.durations[testCase.expectedIndex]}, | |
| std::nullopt, odataId, testCase.expectedIndex); | |
| } else { | |
| checkLogEntries(logEntryArray, {}, {}, testCase.rebooting, odataId, 0); | |
| } | |
| } | |
| } | |
| TEST_F(SnapshotFixture, GetSystemBootLogEntry) { | |
| const std::string odataId = | |
| "/redfish/v1/Systems/system/LogServices/BootTime/Entries"; | |
| testBootLogEntry(app_, CreateRequest(), share_async_resp_, "system", "host0", | |
| odataId, true, RunIoUntilDone); | |
| } | |
| TEST_F(SnapshotFixture, GetManagerBootLogEntry) { | |
| const std::string odataId = | |
| "/redfish/v1/Managers/bmc/LogServices/BootTime/Entries"; | |
| testBootLogEntry(app_, CreateRequest(), share_async_resp_, "", "bmc", odataId, | |
| false, RunIoUntilDone); | |
| } | |
| #ifdef BMCWEB_ENABLE_RASMANAGER_EVENT_LOG | |
| // NOLINTNEXTLINE(runtime/string) | |
| const std::string RasManagerLogName = | |
| "/com/intel/ras/logs/Correctable_1_1728369268114"; | |
| std::shared_ptr<ValueType> CreateMockRasManagerEntry() { | |
| MapperGetSubTreePathsResponse mockRasManagerEntry{{RasManagerLogName}}; | |
| return managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockRasManagerEntry)); | |
| } | |
| std::shared_ptr<ValueType> CreateMockRasManagerLogEntry() { | |
| DBusPropertiesMap mockRasManagerLogEntry{ | |
| {std::make_pair("TimestampMs", 1728369268114ULL)}}; | |
| return managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockRasManagerLogEntry)); | |
| } | |
| TEST_F(SnapshotFixture, LogServiceRasManagerEntriesGetSuccessfulResponse) { | |
| std::string systemId = "system"; | |
| // kManagedSubtreePaths|/com/intel/ras/logs|0|xyz.openbmc_project.Time.EpochTime | |
| KeyType key(ManagedType::kManagedSubtreePaths, "/com/intel/ras/logs", 0, | |
| {"xyz.openbmc_project.Time.EpochTime"}); | |
| // insert for handleLogServicesRasManagerEntriesCollectionGet | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore(key, CreateMockRasManagerEntry()) | |
| .ok()); | |
| // kManagedPropertyMap|com.intel.RAS|/com/intel/ras/logs/Correctable_1_1728369268114|xyz.openbmc_project.Time.EpochTime | |
| KeyType key2(ManagedType::kManagedPropertyMap, "com.intel.RAS", | |
| RasManagerLogName, "xyz.openbmc_project.Time.EpochTime"); | |
| // insert for logRasManagerEntry | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| key2, CreateMockRasManagerLogEntry()) | |
| .ok()); | |
| std::string filePath = | |
| "/tmp/ras-manager/output/Correctable_1_1728369268114.cpr"; | |
| std::filesystem::path dirPath = std::filesystem::path(filePath).parent_path(); | |
| std::filesystem::create_directories(dirPath); | |
| std::ofstream file(filePath); | |
| file << "CPER" << '\n'; | |
| file.close(); | |
| handleLogServicesRasManagerEntriesCollectionGet(app_, CreateRequest(), | |
| share_async_resp_, systemId); | |
| RunIoUntilDone(); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["@odata.id"], "/redfish/v1/Systems/" + systemId + | |
| "/LogServices/HostCper/Entries"); | |
| EXPECT_EQ(json["@odata.type"], "#LogEntryCollection.LogEntryCollection"); | |
| EXPECT_EQ(json["Description"], "Collection of RasManager Entries"); | |
| EXPECT_EQ(json["Name"], "Open BMC RasManager Entries"); | |
| EXPECT_EQ(json["Members"].size(), json["Members@odata.count"]); | |
| // Hardcoding the verification of the Members check | |
| nlohmann::json& rasLog1 = json["Members"][0]; | |
| EXPECT_EQ(rasLog1["@odata.id"], | |
| "/redfish/v1/Systems/" + systemId + | |
| "/LogServices/HostCper/Entries/Correctable_1_1728369268114"); | |
| EXPECT_EQ(rasLog1["@odata.type"], "#LogEntry.v1_9_0.LogEntry"); | |
| EXPECT_EQ(rasLog1["Created"], "2024-10-08T06:34:28.114+00:00"); | |
| EXPECT_EQ(rasLog1["DiagnosticData"], "Q1BFUgo="); | |
| EXPECT_EQ(rasLog1["DiagnosticDataType"], "CPER"); | |
| EXPECT_EQ(rasLog1["EntryType"], "Oem"); | |
| EXPECT_EQ(rasLog1["Id"], "Correctable_1_1728369268114"); | |
| EXPECT_EQ(rasLog1["Name"], "RasManager"); | |
| EXPECT_EQ(rasLog1["OemRecordFormat"], "CPER"); | |
| // Response validation | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| } | |
| #endif // BMCWEB_ENABLE_RASMANAGER_EVENT_LOG | |
| #ifdef BMCWEB_ENABLE_PPR | |
| TEST_F(SnapshotFixture, LogServicehandlePPRServiceGet) { | |
| handlePPRServiceGet(app_, CreateRequest(), share_async_resp_); | |
| RunIoUntilDone(); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["@odata.id"], | |
| "/redfish/v1/Systems/system/LogServices/Oem/Amd/PostPackageRepair"); | |
| EXPECT_EQ(json["Members"].size(), json["Members@odata.count"]); | |
| EXPECT_GT(json["Members@odata.count"], 0); | |
| EXPECT_GT(json["Actions"], 0); | |
| } | |
| constexpr char const* pprFileInterface = | |
| "xyz.openbmc_project.PostPackageRepair.PprData"; | |
| constexpr char const* pprFileObject = "xyz.openbmc_project.PostPackageRepair"; | |
| constexpr char const* pprFilePath = "/xyz/openbmc_project/PostPackageRepair"; | |
| constexpr uint16_t pprStatusRepairEntryNum = 1; | |
| constexpr uint16_t pprStatusRepairType = 2; | |
| constexpr uint16_t pprStatusSocNum = 3; | |
| constexpr uint16_t pprStatusRepairResult = 4; | |
| std::vector<uint16_t> pprStatusPayload = {5, 6}; | |
| constexpr char const* pprFileRequestJson = | |
| "{\"RepairType\":2, \"RepairEntryNum\":1, \"SocNum\":3, \"Payload\":[5, " | |
| "6]}"; | |
| constexpr char const* pprConfigRequestJson = | |
| "{\"autoScheduleBtAsHard\":true, \"autoScheduleRtAsBtPpr\":false}"; | |
| void setPPRStatus(bool dbusException) { | |
| const PostPackageRepairStatusParam pprParam{ | |
| {{ | |
| pprStatusRepairEntryNum, | |
| pprStatusRepairType, | |
| pprStatusSocNum, | |
| pprStatusRepairResult, | |
| pprStatusPayload, | |
| }}, | |
| }; | |
| if (dbusException) { | |
| EXPECT_CALL(*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const PostPackageRepairStatusParam&)>&&>(), | |
| pprFileObject, pprFilePath, pprFileInterface, | |
| "getPostPackageRepairStatus")) | |
| .Times(1) | |
| .WillOnce( | |
| SimulateFailedAsyncPostDbusCallThreadSafeWithEmptyValueAction:: | |
| SimulateFailedAsyncPostDbusCallWithEmptyValue()); | |
| } else { | |
| EXPECT_CALL(*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const PostPackageRepairStatusParam&)>&&>(), | |
| pprFileObject, pprFilePath, pprFileInterface, | |
| "getPostPackageRepairStatus")) | |
| .Times(1) | |
| .WillOnce( | |
| SimulateSuccessfulAsyncPostDbusCallThreadSafeWithValueAction< | |
| PostPackageRepairStatusParam>:: | |
| SimulateSuccessfulAsyncPostDbusCallWithValue( | |
| std::make_shared<PostPackageRepairStatusParam>(pprParam))); | |
| } | |
| } | |
| TEST_F(SnapshotFixture, FailedGetPostPackageRepairStatus) { | |
| setPPRStatus(true); | |
| getPostPackageRepairStatus(share_async_resp_); | |
| // Wait Dbus call complete | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::internal_server_error); | |
| } | |
| TEST_F(SnapshotFixture, SuccessfullyGetPostPackageRepairStatus) { | |
| setPPRStatus(false); | |
| getPostPackageRepairStatus(share_async_resp_); | |
| // Wait Dbus call complete | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| } | |
| TEST_F(SnapshotFixture, FailedhandlePPRStatusGet) { | |
| setPPRStatus(true); | |
| handlePPRStatusGet(app_, CreateRequest(), share_async_resp_); | |
| // Wait Dbus call complete | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::internal_server_error); | |
| } | |
| TEST_F(SnapshotFixture, SuccessfullyhandlePPRStatusGet) { | |
| setPPRStatus(false); | |
| handlePPRStatusGet(app_, CreateRequest(), share_async_resp_); | |
| // Wait Dbus call complete | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_GT(json["Members@odata.count"], 0); | |
| EXPECT_TRUE(json["Members"][0].contains("repairEntryNum")); | |
| EXPECT_EQ(json["Members"][0]["repairEntryNum"], pprStatusRepairEntryNum); | |
| EXPECT_TRUE(json["Members"][0].contains("repairType")); | |
| EXPECT_EQ(json["Members"][0]["repairType"], pprStatusRepairType); | |
| EXPECT_TRUE(json["Members"][0].contains("socNum")); | |
| EXPECT_EQ(json["Members"][0]["socNum"], pprStatusSocNum); | |
| EXPECT_TRUE(json["Members"][0].contains("repairResult")); | |
| EXPECT_EQ(json["Members"][0]["repairResult"], pprStatusRepairResult); | |
| EXPECT_TRUE(json["Members"][0].contains("payload")); | |
| EXPECT_EQ(json["Members"][0]["payload"], pprStatusPayload); | |
| } | |
| void prepareSetPostPackageRepairDataExpectCall( | |
| int dbusException /*0 for no error*/) { | |
| const bool recordAdd = true; | |
| const uint32_t startRuntimeRepair = 2; | |
| if (dbusException >= 3) { | |
| EXPECT_CALL(*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void(const boost::system::error_code&, | |
| const bool&)>&&>(), | |
| pprFileObject, pprFilePath, pprFileInterface, | |
| "setPostPackageRepairData", pprStatusRepairEntryNum, | |
| pprStatusRepairType, pprStatusSocNum, pprStatusPayload)) | |
| .Times(testing::AtMost(1)) | |
| .WillOnce( | |
| SimulateFailedAsyncPostDbusCallThreadSafeWithEmptyValueAction:: | |
| SimulateFailedAsyncPostDbusCallWithEmptyValue()); | |
| return; | |
| } | |
| EXPECT_CALL(*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void(const boost::system::error_code&, | |
| const bool&)>&&>(), | |
| pprFileObject, pprFilePath, pprFileInterface, | |
| "setPostPackageRepairData", pprStatusRepairEntryNum, | |
| pprStatusRepairType, pprStatusSocNum, pprStatusPayload)) | |
| .Times(1) | |
| .WillOnce( | |
| SimulateSuccessfulAsyncPostDbusCallThreadSafeWithValueAction<bool>:: | |
| SimulateSuccessfulAsyncPostDbusCallWithValue( | |
| std::make_shared<bool>(recordAdd))); | |
| if (dbusException >= 2) { | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| setProperty(pprFileObject, pprFilePath, pprFileInterface, "RecordAdd", | |
| dbus::utility::DbusVariantType{true}, _)) | |
| .Times(1) | |
| .WillOnce(SimulateFailedAsyncSetPropertyDbusCallAction:: | |
| SimulateFailedAsyncSetPropertyDbusCall()); | |
| return; | |
| } | |
| EXPECT_CALL(*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| setProperty(pprFileObject, pprFilePath, pprFileInterface, | |
| "RecordAdd", dbus::utility::DbusVariantType{true}, _)) | |
| .Times(1) | |
| .WillOnce(SimulateSuccessfulAsyncSetPropertyDbusCallAction:: | |
| SimulateSuccessfulAsyncSetPropertyDbusCall()); | |
| if (dbusException >= 1) { | |
| EXPECT_CALL(*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void(const boost::system::error_code&, | |
| const uint32_t&)>&&>(), | |
| pprFileObject, pprFilePath, pprFileInterface, | |
| "startRuntimeRepair", 0)) | |
| .Times(testing::AtMost(1)) | |
| .WillOnce( | |
| SimulateFailedAsyncPostDbusCallThreadSafeWithEmptyValueAction:: | |
| SimulateFailedAsyncPostDbusCallWithEmptyValue()); | |
| return; | |
| } | |
| EXPECT_CALL(*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void(const boost::system::error_code&, | |
| const uint32_t&)>&&>(), | |
| pprFileObject, pprFilePath, pprFileInterface, | |
| "startRuntimeRepair", 0)) | |
| .Times(testing::AtMost(1)) | |
| .WillOnce(SimulateSuccessfulAsyncPostDbusCallThreadSafeWithValueAction< | |
| uint32_t>:: | |
| SimulateSuccessfulAsyncPostDbusCallWithValue( | |
| std::make_shared<uint32_t>(startRuntimeRepair))); | |
| } | |
| TEST_F(SnapshotFixture, FailedSetPostPackageRepairData) { | |
| prepareSetPostPackageRepairDataExpectCall(3); | |
| setPostPackageRepairData(share_async_resp_, 0 /*Index*/, | |
| pprStatusRepairEntryNum, pprStatusRepairType, | |
| pprStatusSocNum, pprStatusPayload); | |
| // Wait Dbus call complete | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::internal_server_error); | |
| } | |
| TEST_F(SnapshotFixture, setPostPackageRepairDataFailedSetProperty) { | |
| prepareSetPostPackageRepairDataExpectCall(2); | |
| setPostPackageRepairData(share_async_resp_, 0 /*Index*/, | |
| pprStatusRepairEntryNum, pprStatusRepairType, | |
| pprStatusSocNum, pprStatusPayload); | |
| // Wait Dbus call complete | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::internal_server_error); | |
| } | |
| TEST_F(SnapshotFixture, setPostPackageRepairDataFailedStartRuntimeRepair) { | |
| prepareSetPostPackageRepairDataExpectCall(1); | |
| setPostPackageRepairData(share_async_resp_, 0 /*Index*/, | |
| pprStatusRepairEntryNum, pprStatusRepairType, | |
| pprStatusSocNum, pprStatusPayload); | |
| // Wait Dbus call complete | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::internal_server_error); | |
| } | |
| TEST_F(SnapshotFixture, SuccessfullySetPostPackageRepairData) { | |
| prepareSetPostPackageRepairDataExpectCall(0); | |
| setPostPackageRepairData(share_async_resp_, 0 /*Index*/, | |
| pprStatusRepairEntryNum, pprStatusRepairType, | |
| pprStatusSocNum, pprStatusPayload); | |
| // Wait Dbus call complete | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| } | |
| TEST_F(SnapshotFixture, FailedOobPprEnableHandlePPRFilePost) { | |
| // fail because oobPprEnable == false | |
| KeyType key(ManagedType::kManagedProperty, pprFileObject, | |
| sdbusplus::message::object_path(pprFilePath), pprFileInterface, | |
| "oobPprEnable"); | |
| std::shared_ptr<ValueType> oobPprEnable = | |
| CreateValueType(DbusVariantType(false)); | |
| EXPECT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore(key, oobPprEnable) | |
| .ok()); | |
| handlePPRFilePost(app_, CreateRequest(pprFileRequestJson), share_async_resp_); | |
| // Wait Dbus call complete | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::internal_server_error); | |
| } | |
| TEST_F(SnapshotFixture, SuccessfullyHandlePPRFilePost) { | |
| KeyType key(ManagedType::kManagedProperty, pprFileObject, | |
| sdbusplus::message::object_path(pprFilePath), pprFileInterface, | |
| "oobPprEnable"); | |
| std::shared_ptr<ValueType> oobPprEnable = | |
| CreateValueType(DbusVariantType(true)); | |
| EXPECT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore(key, oobPprEnable) | |
| .ok()); | |
| prepareSetPostPackageRepairDataExpectCall(0); | |
| handlePPRFilePost(app_, CreateRequest(pprFileRequestJson), share_async_resp_); | |
| // Wait Dbus call complete | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| } | |
| TEST_F(SnapshotFixture, FailedGetPostPackageRepairConfig) { | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void(const boost::system::error_code&, | |
| const std::vector<uint16_t>&)>&&>(), | |
| pprFileObject, pprFilePath, pprFileInterface, | |
| "getPostPackageRepairConfig")) | |
| .Times(testing::AtMost(1)) | |
| .WillOnce(SimulateFailedAsyncPostDbusCallThreadSafeWithEmptyValueAction:: | |
| SimulateFailedAsyncPostDbusCallWithEmptyValue()); | |
| getPostPackageRepairConfig(share_async_resp_); | |
| // Wait Dbus call complete | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::internal_server_error); | |
| } | |
| TEST_F(SnapshotFixture, SuccessfullyGetPostPackageRepairConfig) { | |
| const uint16_t oobPprEnable = 1; // true | |
| const uint16_t RtToBt = 0; // false | |
| const uint16_t BtSetToHard = 0; // false | |
| const std::vector<uint16_t> config{{oobPprEnable, RtToBt, BtSetToHard}}; | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void(const boost::system::error_code&, | |
| const std::vector<uint16_t>&)>&&>(), | |
| pprFileObject, pprFilePath, pprFileInterface, | |
| "getPostPackageRepairConfig")) | |
| .Times(testing::AtMost(1)) | |
| .WillOnce(SimulateSuccessfulAsyncPostDbusCallThreadSafeWithValueAction< | |
| std::vector<uint16_t>>:: | |
| SimulateSuccessfulAsyncPostDbusCallWithValue( | |
| std::make_shared<std::vector<uint16_t>>(config))); | |
| getPostPackageRepairConfig(share_async_resp_); | |
| // Wait Dbus call complete | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_GT(json["Members@odata.count"], 0); | |
| EXPECT_TRUE(json["Members"][0].contains("OobPprEnable")); | |
| EXPECT_TRUE(json["Members"][0].contains("autoScheduleRtAsBtPpr")); | |
| EXPECT_TRUE(json["Members"][0].contains("autoScheduleBtAsHard")); | |
| EXPECT_EQ(json["Members"][0]["OobPprEnable"], true); | |
| EXPECT_EQ(json["Members"][0]["autoScheduleRtAsBtPpr"], false); | |
| EXPECT_EQ(json["Members"][0]["autoScheduleBtAsHard"], false); | |
| } | |
| TEST_F(SnapshotFixture, SuccessfullyhandlePPRConfigGet) { | |
| const uint16_t oobPprEnable = 1; // true | |
| const uint16_t RtToBt = 0; // false | |
| const uint16_t BtSetToHard = 0; // false | |
| const std::vector<uint16_t> config{{oobPprEnable, RtToBt, BtSetToHard}}; | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void(const boost::system::error_code&, | |
| const std::vector<uint16_t>&)>&&>(), | |
| pprFileObject, pprFilePath, pprFileInterface, | |
| "getPostPackageRepairConfig")) | |
| .Times(testing::AtMost(1)) | |
| .WillOnce(SimulateSuccessfulAsyncPostDbusCallThreadSafeWithValueAction< | |
| std::vector<uint16_t>>:: | |
| SimulateSuccessfulAsyncPostDbusCallWithValue( | |
| std::make_shared<std::vector<uint16_t>>(config))); | |
| handlePPRConfigGet(app_, CreateRequest(), share_async_resp_); | |
| // Wait Dbus call complete | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["@odata.id"], | |
| "/redfish/v1/Systems/system/LogServices" | |
| "/Oem/Amd/PostPackageRepair/Config"); | |
| EXPECT_GT(json["Members@odata.count"], 0); | |
| EXPECT_TRUE(json["Members"][0].contains("OobPprEnable")); | |
| EXPECT_TRUE(json["Members"][0].contains("autoScheduleRtAsBtPpr")); | |
| EXPECT_TRUE(json["Members"][0].contains("autoScheduleBtAsHard")); | |
| EXPECT_EQ(json["Members"][0]["OobPprEnable"], true); | |
| EXPECT_EQ(json["Members"][0]["autoScheduleRtAsBtPpr"], false); | |
| EXPECT_EQ(json["Members"][0]["autoScheduleBtAsHard"], false); | |
| } | |
| TEST_F(SnapshotFixture, FailedSetPostPackageConfig) { | |
| const uint16_t flag = 0x01; // kBtSetToHardMask | |
| const bool data = true; | |
| EXPECT_CALL(*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void(const boost::system::error_code&, | |
| const bool&)>&&>(), | |
| pprFileObject, pprFilePath, pprFileInterface, | |
| "setPostPackageRepairConfig", flag, data)) | |
| .Times(testing::AtMost(1)) | |
| .WillOnce(SimulateFailedAsyncPostDbusCallThreadSafeWithEmptyValueAction:: | |
| SimulateFailedAsyncPostDbusCallWithEmptyValue()); | |
| setPostPackageRepairConfig(share_async_resp_, flag, data); | |
| // Wait Dbus call complete | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::internal_server_error); | |
| } | |
| TEST_F(SnapshotFixture, SuccessfullyhandlePPRConfigPost) { | |
| EXPECT_CALL(*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void(const boost::system::error_code&, | |
| const bool&)>&&>(), | |
| pprFileObject, pprFilePath, pprFileInterface, | |
| "setPostPackageRepairConfig", 0x02, false)) | |
| .Times(testing::AtMost(1)) | |
| .WillOnce( | |
| SimulateSuccessfulAsyncPostDbusCallThreadSafeWithValueAction<bool>:: | |
| SimulateSuccessfulAsyncPostDbusCallWithValue( | |
| std::make_shared<bool>(true))); | |
| handlePPRConfigPost(app_, CreateRequest(pprConfigRequestJson), | |
| share_async_resp_); | |
| // Wait Dbus call complete | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| } | |
| #endif // BMCWEB_ENABLE_PPR | |
| std::shared_ptr<ValueType> CreateMockFaultLogCPEREntry() { | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/entry/2"), | |
| DBusInteracesMap{ | |
| {// First Mock Completed Status | |
| std::make_pair( | |
| "xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")))}}), | |
| std::make_pair("xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(0)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("2")))}})}})}, | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/entry/1"), | |
| DBusInteracesMap{ | |
| {// First Mock Completed Status | |
| std::make_pair( | |
| "xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair("Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.CPER"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("1")))}})}})}, | |
| }; | |
| return managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry)); | |
| } | |
| std::shared_ptr<ValueType> CreateMockFaultLogMetisCPEREntry() { | |
| std::string primaryLogId = | |
| (std::filesystem::temp_directory_path() / "mock_metis_cper.bin").string(); | |
| ManagedObjectType mockFaultLogEntry{{std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/0/entry/1"), | |
| DBusInteracesMap{ | |
| // Completed Status | |
| {std::make_pair("xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")}})}, | |
| // OriginatedBy | |
| {std::make_pair( | |
| "xyz.openbmc_project.Common.OriginatedBy", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "OriginatorId", | |
| "/redfish/v1/Chassis/Humubeach/PCIeDevices/Metis_0"), | |
| std::make_pair("OriginatorType", | |
| "xyz.openbmc_project.Common.OriginatedBy." | |
| "OriginatorTypes.Client")}})}, | |
| // FaultLog entry properties | |
| {std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair("Type", | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.MetisCPER"), | |
| std::make_pair("PrimaryLogId", primaryLogId)}})}})}}; | |
| return managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry)); | |
| } | |
| TEST_F(SnapshotFixture, | |
| LogServiceFaultLogEntriesGetMetisCPERSuccessfulResponse) { | |
| std::string systemId = "system1"; | |
| KeyType key(ManagedType::kManagedObject, "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| key, CreateMockFaultLogMetisCPEREntry()) | |
| .ok()); | |
| handleLogServiceFaultLogEntriesGet(app_, CreateRequest(), share_async_resp_, | |
| systemId); | |
| RunIoUntilDone(); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["Members@odata.count"], 1); | |
| nlohmann::json& faultLog1 = json["Members"][0]; | |
| EXPECT_EQ(faultLog1["@odata.id"], | |
| "/redfish/v1/Systems/system1/LogServices/FaultLog/Entries/1"); | |
| EXPECT_EQ(faultLog1["DiagnosticDataType"], "OEM"); | |
| EXPECT_EQ(faultLog1["AdditionalDataURI"], | |
| "/redfish/v1/Systems/system1/LogServices/FaultLog/Entries/1"); | |
| EXPECT_EQ(faultLog1["OEMDiagnosticDataType"], "MetisCPER"); | |
| EXPECT_EQ(faultLog1["OemRecordFormat"], "MetisCPER"); | |
| EXPECT_EQ(faultLog1["Links"]["OriginOfCondition"]["@odata.id"], | |
| "/redfish/v1/Chassis/Humubeach/PCIeDevices/Metis_0"); | |
| EXPECT_EQ(faultLog1["Links"]["RelatedLogEntries"][0]["@odata.id"], | |
| "/redfish/v1/Systems/system1/LogServices/FaultLog/Entries/1"); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| } | |
| TEST_F(SnapshotFixture, | |
| LogServiceFaultLogEntryByIdMetisCPERSuccessfulResponse) { | |
| std::string systemId = "system1"; | |
| KeyType key(ManagedType::kManagedObject, "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| key, CreateMockFaultLogMetisCPEREntry()) | |
| .ok()); | |
| std::filesystem::path attachmentPath = | |
| std::filesystem::temp_directory_path() / "mock_metis_cper.bin"; | |
| std::ofstream ofs(attachmentPath, std::ios::out | std::ios::binary); | |
| ASSERT_TRUE(ofs.is_open()); | |
| std::string_view expectedData = "BinaryMetisCPERPayload_0123456789"; | |
| ofs.write(expectedData.data(), static_cast<int>(expectedData.size())); | |
| ofs.close(); | |
| getDumpEntryById( | |
| share_async_resp_, "1", | |
| Dump{.type = DumpType::SYSTEM_FAULT_LOG, .systemName = systemId}); | |
| RunIoUntilDone(); | |
| nlohmann::json& faultLog1 = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(faultLog1["@odata.id"], | |
| "/redfish/v1/Systems/system1/LogServices/FaultLog/Entries/1"); | |
| EXPECT_EQ(faultLog1["DiagnosticDataType"], "OEM"); | |
| EXPECT_EQ(faultLog1["AdditionalDataURI"], | |
| "/redfish/v1/Systems/system1/LogServices/FaultLog/Entries/1"); | |
| EXPECT_EQ(faultLog1["OEMDiagnosticDataType"], "MetisCPER"); | |
| EXPECT_EQ(faultLog1["OemRecordFormat"], "MetisCPER"); | |
| EXPECT_EQ(faultLog1["Links"]["OriginOfCondition"]["@odata.id"], | |
| "/redfish/v1/Chassis/Humubeach/PCIeDevices/Metis_0"); | |
| EXPECT_EQ(faultLog1["Links"]["RelatedLogEntries"][0]["@odata.id"], | |
| "/redfish/v1/Systems/system1/LogServices/FaultLog/Entries/1"); | |
| EXPECT_EQ(faultLog1["DiagnosticData"], | |
| "QmluYXJ5TWV0aXNDUEVSUGF5bG9hZF8wMTIzNDU2Nzg5"); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| std::filesystem::remove(attachmentPath); | |
| } | |
| TEST_F(SnapshotFixture, QueryCrashDumpCollectionExpansion) { | |
| KeyType key1(ManagedType::kManagedObject, "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| key1, CreateMockFaultLogCPEREntry()) | |
| .ok()); | |
| query_param::Query delegatedQuery; | |
| delegatedQuery.expandLevel = 2; | |
| std::string_view fileContent = "Q1BFUgEB/////wEAAgAAA"; | |
| BMCWEB_ROUTE(app_, "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/") | |
| .privileges({{"ConfigureComponents"}}) | |
| .methods(boost::beast::http::verb::get)( | |
| [this, &fileContent]( | |
| const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| if (!redfish::setUpRedfishRoute(app_, req, asyncResp)) { | |
| return; | |
| } | |
| boost::asio::post( | |
| GetStaticIoContext(), [&fileContent, asyncResp, systemName]() { | |
| nlohmann::json& thisEntry = asyncResp->res.jsonValue; | |
| thisEntry["@odata.id"] = "/redfish/v1/Systems/" + systemName + | |
| "/LogServices/Crashdump/Entries"; | |
| thisEntry["@odata.type"] = | |
| "#LogEntryCollection.LogEntryCollection"; | |
| thisEntry["Description"] = "Collection of Host Dump Entries"; | |
| thisEntry["Members"] = nlohmann::json::array(); | |
| thisEntry["Members@odata.count"] = 1; | |
| thisEntry["Name"] = "Host Dump Entries"; | |
| nlohmann::json dumpEntry; | |
| dumpEntry["@odata.id"] = | |
| "/redfish/v1/Systems/" + systemName + | |
| "/LogServices/Crashdump/Entries/dumpmanager_2"; | |
| dumpEntry["@odata.type"] = "#LogEntry.v1_11_0.LogEntry"; | |
| dumpEntry["Created"] = "2024-08-26T20:21:09.735414+00:00"; | |
| dumpEntry["DiagnosticData"] = fileContent; | |
| dumpEntry["DiagnosticDataType"] = "OpenBMC Fault Log"; | |
| dumpEntry["EntryType"] = "Oem"; | |
| dumpEntry["Id"] = "2"; | |
| dumpEntry["AdditionalDataURI"] = | |
| "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/2"; | |
| dumpEntry["Name"] = "FaultLog Dump Entry"; | |
| dumpEntry["OemRecordFormat"] = "Crashdump"; | |
| thisEntry["Members"].push_back(std::move(dumpEntry)); | |
| }); | |
| }); | |
| BMCWEB_ROUTE(app_, "/redfish/v1/Systems/<str>/LogServices/HostCper/Entries/") | |
| .privileges({{"ConfigureComponents"}}) | |
| .methods(boost::beast::http::verb::get)( | |
| [this, &fileContent]( | |
| const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemId) { | |
| if (!redfish::setUpRedfishRoute(app_, req, asyncResp)) { | |
| return; | |
| } | |
| boost::asio::post(GetStaticIoContext(), [&fileContent, asyncResp, | |
| systemId]() { | |
| nlohmann::json& thisEntry = asyncResp->res.jsonValue; | |
| thisEntry["@odata.id"] = "/redfish/v1/Systems/" + systemId + | |
| "/LogServices/HostCper/Entries"; | |
| thisEntry["@odata.type"] = | |
| "#LogEntryCollection.LogEntryCollection"; | |
| thisEntry["Description"] = "Collection of Host CPER Entries"; | |
| thisEntry["Members"] = nlohmann::json::array(); | |
| thisEntry["Members@odata.count"] = 1; | |
| thisEntry["Name"] = "Host CPER Entries"; | |
| nlohmann::json hostCperEntry; | |
| hostCperEntry["@odata.id"] = "/redfish/v1/Systems/" + systemId + | |
| "/LogServices/HostCper/Entries/1"; | |
| hostCperEntry["@odata.type"] = "#LogEntry.v1_11_0.LogEntry"; | |
| hostCperEntry["Created"] = "2024-08-26T20:21:09.735414+00:00"; | |
| hostCperEntry["DiagnosticData"] = fileContent; | |
| hostCperEntry["DiagnosticDataType"] = "CPER"; | |
| hostCperEntry["EntryType"] = "Oem"; | |
| hostCperEntry["Id"] = "1"; | |
| hostCperEntry["Name"] = "Host CPER Entry"; | |
| thisEntry["Members"].push_back(std::move(hostCperEntry)); | |
| }); | |
| }); | |
| app_.validate(); | |
| getDumpEntryCollection(app_, CreateRequest(), share_async_resp_, | |
| Dump{.type = DumpType::BMC_FAULT_LOG}, delegatedQuery); | |
| RunIoUntilDone(); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["@odata.id"], | |
| "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries"); | |
| EXPECT_EQ(json["@odata.type"], "#LogEntryCollection.LogEntryCollection"); | |
| EXPECT_EQ(json["Description"], "Collection of FaultLog Dump Entries"); | |
| EXPECT_EQ(json["Name"], "FaultLog Dump Entries"); | |
| EXPECT_EQ(json["Members"].size(), json["Members@odata.count"]); | |
| // Expected data. from odata.id : {field : value}. | |
| // Example: | |
| // - /redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/2: | |
| // - id: 2 | |
| // - OemRecordFormat: Crashdump | |
| std::unordered_map<std::string, std::unordered_map<std::string, std::string>> | |
| expectedData = { | |
| {"/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/2", | |
| { | |
| {"Id", "2"}, | |
| {"OemRecordFormat", "Crashdump"}, | |
| }}, | |
| {"/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/1", | |
| { | |
| {"Id", "1"}, | |
| {"OemRecordFormat", "CPER"}, | |
| }}}; | |
| EXPECT_TRUE(json.contains("Members")); | |
| EXPECT_EQ(json["Members"].size(), 2); | |
| for (auto& member : json["Members"]) { | |
| std::string odataId = member["@odata.id"]; | |
| EXPECT_TRUE(expectedData.contains(odataId)); | |
| auto& data = expectedData[odataId]; | |
| EXPECT_EQ(member["Id"], data["Id"]); | |
| EXPECT_EQ(member["OemRecordFormat"], data["OemRecordFormat"]); | |
| } | |
| } | |
| std::shared_ptr<ValueType> CreateMockFaultLogMixedCrashdumpEntries() { | |
| ManagedObjectType mockFaultLogEntry{ | |
| // System-specific entry for system1 | |
| // (/xyz/openbmc_project/dump/faultlog/0/entry/1) | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/0/entry/1"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")))}}), | |
| std::make_pair("xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(0)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("1")))}})}})}, | |
| // System-agnostic entry (/xyz/openbmc_project/dump/faultlog/entry/2) | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/entry/2"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")))}}), | |
| std::make_pair("xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(0)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("2")))}})}})}, | |
| // System-specific entry for system2 | |
| // (/xyz/openbmc_project/dump/faultlog/1/entry/3) | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/1/entry/3"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")))}}), | |
| std::make_pair("xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(0)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("3")))}})}})}, | |
| }; | |
| return managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry)); | |
| } | |
| TEST_F(SnapshotFixture, | |
| LogServiceFaultLogEntriesGetSystemSpecificWithPrefixes) { | |
| std::string systemId = "system1"; | |
| KeyType key(ManagedType::kManagedObject, "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| key, CreateMockFaultLogMixedCrashdumpEntries()) | |
| .ok()); | |
| handleLogServiceFaultLogEntriesGet(app_, CreateRequest(), share_async_resp_, | |
| systemId); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system1/LogServices/FaultLog/Entries", | |
| "@odata.type": "#LogEntryCollection.LogEntryCollection", | |
| "Description": "Collection of FaultLog Dump Entries", | |
| "Members": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system1/LogServices/FaultLog/Entries/1", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "AdditionalDataURI": "/redfish/v1/Systems/system1/LogServices/Crashdump/Entries/dumpmanager_system1_1", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticDataType": "OEM", | |
| "EntryType": "Oem", | |
| "Id": "1", | |
| "Links": { | |
| "RelatedLogEntries": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system1/LogServices/Crashdump/Entries/dumpmanager_system1_1" | |
| } | |
| ] | |
| }, | |
| "Name": "FaultLog Dump Entry", | |
| "OEMDiagnosticDataType": "OpenBMC Fault Log", | |
| "OemRecordFormat": "Crashdump" | |
| } | |
| ], | |
| "Members@odata.count": 1, | |
| "Name": "FaultLog Dump Entries" | |
| })"); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue, expectedJson); | |
| } | |
| TEST_F(SnapshotFixture, LogServiceFaultLogEntryByIdSystemSpecificOnly) { | |
| std::string systemId = "system1"; | |
| KeyType key(ManagedType::kManagedObject, "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| key, CreateMockFaultLogMixedCrashdumpEntries()) | |
| .ok()); | |
| // 1. Fetch system1-specific entry "1" -> 200 OK with prefix | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| getDumpEntryById( | |
| asyncResp, "1", | |
| Dump{.type = DumpType::SYSTEM_FAULT_LOG, .systemName = systemId}); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system1/LogServices/FaultLog/Entries/1", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "AdditionalDataURI": "/redfish/v1/Systems/system1/LogServices/Crashdump/Entries/dumpmanager_system1_1", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticDataType": "OEM", | |
| "EntryType": "Oem", | |
| "Id": "1", | |
| "Links": { | |
| "RelatedLogEntries": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system1/LogServices/Crashdump/Entries/dumpmanager_system1_1" | |
| } | |
| ] | |
| }, | |
| "Name": "FaultLog Dump Entry", | |
| "OEMDiagnosticDataType": "OpenBMC Fault Log", | |
| "OemRecordFormat": "Crashdump" | |
| })"); | |
| EXPECT_EQ(asyncResp->res.jsonValue, expectedJson); | |
| } | |
| // 2. Fetch system-agnostic entry "2" via system1 -> should be 404 (skipped) | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| getDumpEntryById( | |
| asyncResp, "2", | |
| Dump{.type = DumpType::SYSTEM_FAULT_LOG, .systemName = systemId}); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::not_found); | |
| } | |
| // 3. Fetch entry "3" (which belongs to system2) via system1 -> should be 404 | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| getDumpEntryById( | |
| asyncResp, "3", | |
| Dump{.type = DumpType::SYSTEM_FAULT_LOG, .systemName = systemId}); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::not_found); | |
| } | |
| } | |
| TEST_F(SnapshotFixture, LogServiceFaultLogEntriesGetSystem2Isolation) { | |
| std::string systemId = "system2"; | |
| KeyType key(ManagedType::kManagedObject, "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| key, CreateMockFaultLogMixedCrashdumpEntries()) | |
| .ok()); | |
| handleLogServiceFaultLogEntriesGet(app_, CreateRequest(), share_async_resp_, | |
| systemId); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system2/LogServices/FaultLog/Entries", | |
| "@odata.type": "#LogEntryCollection.LogEntryCollection", | |
| "Description": "Collection of FaultLog Dump Entries", | |
| "Members": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system2/LogServices/FaultLog/Entries/3", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "AdditionalDataURI": "/redfish/v1/Systems/system2/LogServices/Crashdump/Entries/dumpmanager_system2_3", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticDataType": "OEM", | |
| "EntryType": "Oem", | |
| "Id": "3", | |
| "Links": { | |
| "RelatedLogEntries": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system2/LogServices/Crashdump/Entries/dumpmanager_system2_3" | |
| } | |
| ] | |
| }, | |
| "Name": "FaultLog Dump Entry", | |
| "OEMDiagnosticDataType": "OpenBMC Fault Log", | |
| "OemRecordFormat": "Crashdump" | |
| } | |
| ], | |
| "Members@odata.count": 1, | |
| "Name": "FaultLog Dump Entries" | |
| })"); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue, expectedJson); | |
| } | |
| std::shared_ptr<ValueType> CreateMockSingleSystemFaultLogEntry( | |
| const std::string& logFilePath = "", const std::string& dumpPrefix = "") { | |
| DBusPropertiesMap faultLogProps{ | |
| {std::make_pair("Type", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair("PrimaryLogId", DbusVariantType(std::string("1")))}}; | |
| if (!logFilePath.empty()) { | |
| faultLogProps.emplace_back("Log", DbusVariantType(logFilePath)); | |
| } | |
| if (!dumpPrefix.empty()) { | |
| faultLogProps.emplace_back("DumpPrefix", DbusVariantType(dumpPrefix)); | |
| } | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/entry/1"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")))}}), | |
| std::make_pair("xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(0)))}}), | |
| std::make_pair("xyz.openbmc_project.Dump.Entry.FaultLog", | |
| std::move(faultLogProps))}})}, | |
| }; | |
| return managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry)); | |
| } | |
| TEST_F(SnapshotFixture, SingleSystemFaultLogAndCrashdumpExposedCorrectly) { | |
| std::string systemId = "system"; | |
| // Mock system inventory path for single-system | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| // Create temporary crashdump log file | |
| std::filesystem::path tempLogFile = std::filesystem::temp_directory_path() / | |
| "test_single_system_crashdump.bin"; | |
| { | |
| std::ofstream ofs(tempLogFile, std::ios::binary); | |
| // Crashdump files on the BMC are already base64 encoded. | |
| ofs << "Q3Jhc2hkdW1wUGF5bG9hZDEyMw=="; | |
| } | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, | |
| CreateMockSingleSystemFaultLogEntry(tempLogFile.string())) | |
| .ok()); | |
| // 1. FaultLog Collection GET -> | |
| // /redfish/v1/Systems/system/LogServices/FaultLog/Entries | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| handleLogServiceFaultLogEntriesGet(app_, CreateRequest(), asyncResp, | |
| systemId); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/FaultLog/Entries", | |
| "@odata.type": "#LogEntryCollection.LogEntryCollection", | |
| "Description": "Collection of FaultLog Dump Entries", | |
| "Members": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/FaultLog/Entries/1", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "AdditionalDataURI": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/dumpmanager_1", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticDataType": "OEM", | |
| "EntryType": "Oem", | |
| "Id": "1", | |
| "Links": { | |
| "RelatedLogEntries": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/dumpmanager_1" | |
| } | |
| ] | |
| }, | |
| "Name": "FaultLog Dump Entry", | |
| "OEMDiagnosticDataType": "OpenBMC Fault Log", | |
| "OemRecordFormat": "Crashdump" | |
| } | |
| ], | |
| "Members@odata.count": 1, | |
| "Name": "FaultLog Dump Entries" | |
| })"); | |
| EXPECT_EQ(asyncResp->res.jsonValue, expectedJson); | |
| } | |
| // 2. FaultLog Entry By ID GET -> | |
| // /redfish/v1/Systems/system/LogServices/FaultLog/Entries/1 | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| getDumpEntryById( | |
| asyncResp, "1", | |
| Dump{.type = DumpType::SYSTEM_FAULT_LOG, .systemName = systemId}); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/FaultLog/Entries/1", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "AdditionalDataURI": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/dumpmanager_1", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticDataType": "OEM", | |
| "EntryType": "Oem", | |
| "Id": "1", | |
| "Links": { | |
| "RelatedLogEntries": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/dumpmanager_1" | |
| } | |
| ] | |
| }, | |
| "Name": "FaultLog Dump Entry", | |
| "OEMDiagnosticDataType": "OpenBMC Fault Log", | |
| "OemRecordFormat": "Crashdump" | |
| })"); | |
| EXPECT_EQ(asyncResp->res.jsonValue, expectedJson); | |
| } | |
| // 3. Crashdump Collection GET -> | |
| // /redfish/v1/Systems/system/LogServices/Crashdump/Entries | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| handleGetCrashdumpEntryCollection(app_, CreateRequest(), asyncResp, | |
| systemId); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries", | |
| "@odata.type": "#LogEntryCollection.LogEntryCollection", | |
| "Description": "Collection of Crashdump Entries", | |
| "Members": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/dumpmanager_1", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticData": "Q3Jhc2hkdW1wUGF5bG9hZDEyMw==", | |
| "DiagnosticDataType": "CPER", | |
| "EntryType": "Oem", | |
| "Id": "1", | |
| "Name": "Host Crashdump Entry" | |
| } | |
| ], | |
| "Members@odata.count": 1, | |
| "Name": "Crashdump Entries" | |
| })"); | |
| EXPECT_EQ(asyncResp->res.jsonValue, expectedJson); | |
| } | |
| // 4. Crashdump Entry By ID GET -> | |
| // /redfish/v1/Systems/system/LogServices/Crashdump/Entries/dumpmanager_1 | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| handleGetCrashdumpEntry(app_, CreateRequest(), asyncResp, systemId, | |
| "dumpmanager_1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/dumpmanager_1", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticData": "Q3Jhc2hkdW1wUGF5bG9hZDEyMw==", | |
| "DiagnosticDataType": "CPER", | |
| "EntryType": "Oem", | |
| "Id": "1", | |
| "Name": "Host Crashdump Entry" | |
| })"); | |
| EXPECT_EQ(asyncResp->res.jsonValue, expectedJson); | |
| } | |
| std::filesystem::remove(tempLogFile); | |
| } | |
| // A Dump.Manager FaultLog entry that advertises a DumpPrefix is owned by | |
| // another daemon (e.g. com.intel.crashdump). The FaultLog entry must point at | |
| // that daemon's Crashdump entry, and Dump.Manager must not try to serve the | |
| // crashdump itself. See b/559785933. | |
| TEST_F(SnapshotFixture, SingleSystemFaultLogWithDumpPrefixExposedCorrectly) { | |
| std::string systemId = "system"; | |
| // Mock system inventory path for single-system | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| // The entry has no Log property; the crashdump file is owned by the daemon | |
| // behind the "intel_" prefix. | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, CreateMockSingleSystemFaultLogEntry( | |
| /*logFilePath=*/"", /*dumpPrefix=*/"intel_")) | |
| .ok()); | |
| // 1. FaultLog Collection GET -> | |
| // /redfish/v1/Systems/system/LogServices/FaultLog/Entries | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| handleLogServiceFaultLogEntriesGet(app_, CreateRequest(), asyncResp, | |
| systemId); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/FaultLog/Entries", | |
| "@odata.type": "#LogEntryCollection.LogEntryCollection", | |
| "Description": "Collection of FaultLog Dump Entries", | |
| "Members": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/FaultLog/Entries/1", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "AdditionalDataURI": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/intel_1", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticDataType": "OEM", | |
| "EntryType": "Oem", | |
| "Id": "1", | |
| "Links": { | |
| "RelatedLogEntries": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/intel_1" | |
| } | |
| ] | |
| }, | |
| "Name": "FaultLog Dump Entry", | |
| "OEMDiagnosticDataType": "OpenBMC Fault Log", | |
| "OemRecordFormat": "Crashdump" | |
| } | |
| ], | |
| "Members@odata.count": 1, | |
| "Name": "FaultLog Dump Entries" | |
| })"); | |
| EXPECT_EQ(asyncResp->res.jsonValue, expectedJson); | |
| } | |
| // 2. FaultLog Entry By ID GET -> | |
| // /redfish/v1/Systems/system/LogServices/FaultLog/Entries/1 | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| getDumpEntryById( | |
| asyncResp, "1", | |
| Dump{.type = DumpType::SYSTEM_FAULT_LOG, .systemName = systemId}); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/FaultLog/Entries/1", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "AdditionalDataURI": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/intel_1", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticDataType": "OEM", | |
| "EntryType": "Oem", | |
| "Id": "1", | |
| "Links": { | |
| "RelatedLogEntries": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/intel_1" | |
| } | |
| ] | |
| }, | |
| "Name": "FaultLog Dump Entry", | |
| "OEMDiagnosticDataType": "OpenBMC Fault Log", | |
| "OemRecordFormat": "Crashdump" | |
| })"); | |
| EXPECT_EQ(asyncResp->res.jsonValue, expectedJson); | |
| } | |
| // 3. Crashdump Collection GET -> | |
| // /redfish/v1/Systems/system/LogServices/Crashdump/Entries | |
| // The Dump.Manager entry is skipped instead of failing with an | |
| // InternalError. | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| handleGetCrashdumpEntryCollection(app_, CreateRequest(), asyncResp, | |
| systemId); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries", | |
| "@odata.type": "#LogEntryCollection.LogEntryCollection", | |
| "Description": "Collection of Crashdump Entries", | |
| "Members": [], | |
| "Members@odata.count": 0, | |
| "Name": "Crashdump Entries" | |
| })"); | |
| EXPECT_EQ(asyncResp->res.jsonValue, expectedJson); | |
| } | |
| // 4. Crashdump Entry By ID GET -> | |
| // /redfish/v1/Systems/system/LogServices/Crashdump/Entries/dumpmanager_1 | |
| // The entry is not addressable under the dumpmanager_ prefix anymore. | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| handleGetCrashdumpEntry(app_, CreateRequest(), asyncResp, systemId, | |
| "dumpmanager_1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::not_found); | |
| } | |
| } | |
| // Builds a single Dump.Manager FaultLog Crashdump entry at |objectPath|. | |
| // |primaryLogId| and |dumpPrefix| are omitted from the object when empty. | |
| std::shared_ptr<ValueType> CreateMockFaultLogCrashdumpEntry( | |
| const std::string& objectPath, const std::string& primaryLogId, | |
| const std::string& dumpPrefix) { | |
| DBusPropertiesMap faultLogProps{{std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string("xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump")))}}; | |
| if (!primaryLogId.empty()) { | |
| faultLogProps.emplace_back("PrimaryLogId", DbusVariantType(primaryLogId)); | |
| } | |
| if (!dumpPrefix.empty()) { | |
| faultLogProps.emplace_back("DumpPrefix", DbusVariantType(dumpPrefix)); | |
| } | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path(objectPath), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")))}}), | |
| std::make_pair("xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(0)))}}), | |
| std::make_pair("xyz.openbmc_project.Dump.Entry.FaultLog", | |
| std::move(faultLogProps))}})}, | |
| }; | |
| return managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry)); | |
| } | |
| // DumpPrefix must win over the system-specific "dumpmanager_<system>_" prefix | |
| // on multi-host platforms as well, and the entry must disappear from that | |
| // system's Crashdump collection. See b/559785933. | |
| TEST_F(SnapshotFixture, MultiSystemFaultLogWithDumpPrefixExposedCorrectly) { | |
| std::string systemId = "system1"; | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system1", | |
| "/xyz/openbmc_project/inventory/system/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, CreateMockFaultLogCrashdumpEntry( | |
| "/xyz/openbmc_project/dump/faultlog/0/entry/1", | |
| /*primaryLogId=*/"1", /*dumpPrefix=*/"intel_")) | |
| .ok()); | |
| // 1. FaultLog Entry By ID GET -> | |
| // /redfish/v1/Systems/system1/LogServices/FaultLog/Entries/1 | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| getDumpEntryById( | |
| asyncResp, "1", | |
| Dump{.type = DumpType::SYSTEM_FAULT_LOG, .systemName = systemId}); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system1/LogServices/FaultLog/Entries/1", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "AdditionalDataURI": "/redfish/v1/Systems/system1/LogServices/Crashdump/Entries/intel_1", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticDataType": "OEM", | |
| "EntryType": "Oem", | |
| "Id": "1", | |
| "Links": { | |
| "RelatedLogEntries": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system1/LogServices/Crashdump/Entries/intel_1" | |
| } | |
| ] | |
| }, | |
| "Name": "FaultLog Dump Entry", | |
| "OEMDiagnosticDataType": "OpenBMC Fault Log", | |
| "OemRecordFormat": "Crashdump" | |
| })"); | |
| EXPECT_EQ(asyncResp->res.jsonValue, expectedJson); | |
| } | |
| // 2. Crashdump Collection GET -> | |
| // /redfish/v1/Systems/system1/LogServices/Crashdump/Entries | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| handleGetCrashdumpEntryCollection(app_, CreateRequest(), asyncResp, | |
| systemId); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system1/LogServices/Crashdump/Entries", | |
| "@odata.type": "#LogEntryCollection.LogEntryCollection", | |
| "Description": "Collection of Crashdump Entries", | |
| "Members": [], | |
| "Members@odata.count": 0, | |
| "Name": "Crashdump Entries" | |
| })"); | |
| EXPECT_EQ(asyncResp->res.jsonValue, expectedJson); | |
| } | |
| // 3. Crashdump Entry By ID GET -> | |
| // .../Crashdump/Entries/dumpmanager_system1_1 is no longer addressable. | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| handleGetCrashdumpEntry(app_, CreateRequest(), asyncResp, systemId, | |
| "dumpmanager_system1_1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::not_found); | |
| } | |
| } | |
| // The reproduction in b/559785933 was reported against | |
| // /redfish/v1/Managers/bmc/LogServices/FaultLog/Entries, which reaches the | |
| // same code with Dump::systemName unset. | |
| TEST_F(SnapshotFixture, ManagerFaultLogWithDumpPrefixExposedCorrectly) { | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, CreateMockFaultLogCrashdumpEntry( | |
| "/xyz/openbmc_project/dump/faultlog/entry/1", | |
| /*primaryLogId=*/"1", /*dumpPrefix=*/"intel_")) | |
| .ok()); | |
| query_param::Query delegatedQuery; | |
| getDumpEntryCollection(app_, CreateRequest(), share_async_resp_, | |
| Dump{.type = DumpType::BMC_FAULT_LOG}, delegatedQuery); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries", | |
| "@odata.type": "#LogEntryCollection.LogEntryCollection", | |
| "Description": "Collection of FaultLog Dump Entries", | |
| "Members": [ | |
| { | |
| "@odata.id": "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/1", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "AdditionalDataURI": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/intel_1", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticDataType": "OEM", | |
| "EntryType": "Oem", | |
| "Id": "1", | |
| "Links": { | |
| "RelatedLogEntries": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/intel_1" | |
| } | |
| ] | |
| }, | |
| "Name": "FaultLog Dump Entry", | |
| "OEMDiagnosticDataType": "OpenBMC Fault Log", | |
| "OemRecordFormat": "Crashdump" | |
| } | |
| ], | |
| "Members@odata.count": 1, | |
| "Name": "FaultLog Dump Entries" | |
| })"); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue, expectedJson); | |
| } | |
| // A DumpPrefix without a PrimaryLogId is a daemon-side bug: there is no ID to | |
| // address the owning daemon's entry with. bmcweb still links into the owning | |
| // daemon's ID space (prefix only) and, more importantly, must drop the entry | |
| // from the Crashdump collection instead of failing the whole collection with | |
| // an InternalError. See b/559785933. | |
| TEST_F(SnapshotFixture, FaultLogWithDumpPrefixAndNoPrimaryLogId) { | |
| std::string systemId = "system"; | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, CreateMockFaultLogCrashdumpEntry( | |
| "/xyz/openbmc_project/dump/faultlog/entry/1", | |
| /*primaryLogId=*/"", /*dumpPrefix=*/"intel_")) | |
| .ok()); | |
| // 1. The DumpPrefix still wins over the Dump.Manager prefix, so the link is | |
| // the bare owning-daemon prefix. | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| getDumpEntryById( | |
| asyncResp, "1", | |
| Dump{.type = DumpType::SYSTEM_FAULT_LOG, .systemName = systemId}); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::ok); | |
| EXPECT_EQ( | |
| asyncResp->res.jsonValue["AdditionalDataURI"], | |
| "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/intel_"); | |
| } | |
| // 2. The entry is skipped instead of failing the collection with an | |
| // InternalError. This is the actual fix for b/559785933. | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| handleGetCrashdumpEntryCollection(app_, CreateRequest(), asyncResp, | |
| systemId); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::ok); | |
| EXPECT_EQ(asyncResp->res.jsonValue["Members@odata.count"], 0); | |
| EXPECT_FALSE(asyncResp->res.jsonValue.contains("error")); | |
| } | |
| // 3. The Dump.Manager prefix no longer addresses the entry either. | |
| { | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| handleGetCrashdumpEntry(app_, CreateRequest(), asyncResp, systemId, | |
| "dumpmanager_1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::not_found); | |
| } | |
| } | |
| // The FaultLog entry ID and the PrimaryLogId come from two independently | |
| // allocated ID spaces: the Dump.Manager's and the owning daemon's (here | |
| // com.intel.crashdump). On tme9-nfd01 the FaultLog entry was | |
| // .../faultlog/entry/0 while its payload lived at com.intel.crashdump's entry | |
| // 1, and com.intel.crashdump had both a 0 and a 1, so pointing at "intel_0" | |
| // would have silently served a different crashdump. | |
| // | |
| // Every other DumpPrefix test uses PrimaryLogId == the entry ID, so this is | |
| // the only test that fails if anyone swaps primaryLogId for entryID in the | |
| // DumpPrefix branch of getCrashdumpLogEntryId. It must hold with and without | |
| // BMCWEB_ENABLE_CPER_CRASHDUMP_USE_FAULTLOG_ID. | |
| TEST_F(SnapshotFixture, FaultLogWithDumpPrefixUsesPrimaryLogIdNotEntryId) { | |
| std::string systemId = "system"; | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, CreateMockFaultLogCrashdumpEntry( | |
| "/xyz/openbmc_project/dump/faultlog/entry/0", | |
| /*primaryLogId=*/"1", /*dumpPrefix=*/"intel_")) | |
| .ok()); | |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); | |
| getDumpEntryById( | |
| asyncResp, "0", | |
| Dump{.type = DumpType::SYSTEM_FAULT_LOG, .systemName = systemId}); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(asyncResp->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/FaultLog/Entries/0", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "AdditionalDataURI": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/intel_1", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticDataType": "OEM", | |
| "EntryType": "Oem", | |
| "Id": "0", | |
| "Links": { | |
| "RelatedLogEntries": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/intel_1" | |
| } | |
| ] | |
| }, | |
| "Name": "FaultLog Dump Entry", | |
| "OEMDiagnosticDataType": "OpenBMC Fault Log", | |
| "OemRecordFormat": "Crashdump" | |
| })"); | |
| EXPECT_EQ(asyncResp->res.jsonValue, expectedJson); | |
| } | |
| std::shared_ptr<ValueType> CreateMockBMCDumpEntry() { | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/bmc/entry/1"), | |
| DBusInteracesMap{ | |
| {std::make_pair("xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")}}), | |
| std::make_pair("xyz.openbmc_project.Dump.Entry", | |
| DBusPropertiesMap{ | |
| {std::make_pair("Size", BMC_DUMP_SIZE)}})}})}, | |
| }; | |
| return managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry)); | |
| } | |
| TEST_F(SnapshotFixture, QueryBMCDumpCollectionExpansion) { | |
| KeyType key1(ManagedType::kManagedObject, "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore(key1, CreateMockBMCDumpEntry()) | |
| .ok()); | |
| query_param::Query delegatedQuery; | |
| delegatedQuery.expandLevel = 2; | |
| getDumpEntryCollection(app_, CreateRequest(), share_async_resp_, | |
| Dump{.type = DumpType::BMC_DUMP}, delegatedQuery); | |
| RunIoUntilDone(); | |
| nlohmann::json& resp = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(resp["@odata.id"], | |
| "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"); | |
| EXPECT_EQ(resp["@odata.type"], "#LogEntryCollection.LogEntryCollection"); | |
| EXPECT_EQ(resp["Description"], "Collection of BMC Dump Entries"); | |
| EXPECT_EQ(resp["Members"].size(), resp["Members@odata.count"]); | |
| EXPECT_EQ(resp["Members"].size(), 1); | |
| auto& member = resp["Members"][0]; | |
| EXPECT_EQ(member["AdditionalDataSizeBytes"], BMC_DUMP_SIZE); | |
| EXPECT_EQ(member["EntryType"], "Event"); | |
| EXPECT_EQ(member["Name"], "BMC Dump Entry"); | |
| EXPECT_EQ(member["DiagnosticDataType"], "Manager"); | |
| EXPECT_EQ(member["AdditionalDataURI"], | |
| "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/1/attachment"); | |
| } | |
| std::shared_ptr<ValueType> CreateMockSystemDumpEntry() { | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/system/entry/1"), | |
| DBusInteracesMap{ | |
| {std::make_pair("xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")}}), | |
| std::make_pair("xyz.openbmc_project.Dump.Entry", | |
| DBusPropertiesMap{ | |
| {std::make_pair("Size", BMC_DUMP_SIZE)}})}})}, | |
| }; | |
| return managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry)); | |
| } | |
| TEST_F(SnapshotFixture, QuerySystemDumpCollectionExpansion) { | |
| KeyType key1(ManagedType::kManagedObject, "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore(key1, CreateMockSystemDumpEntry()) | |
| .ok()); | |
| query_param::Query delegatedQuery; | |
| delegatedQuery.expandLevel = 2; | |
| getDumpEntryCollection(app_, CreateRequest(), share_async_resp_, | |
| Dump{.type = DumpType::SYSTEM_DUMP}, delegatedQuery); | |
| RunIoUntilDone(); | |
| nlohmann::json& resp = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(resp["@odata.id"], | |
| "/redfish/v1/Systems/system/LogServices/Dump/Entries"); | |
| EXPECT_EQ(resp["@odata.type"], "#LogEntryCollection.LogEntryCollection"); | |
| EXPECT_EQ(resp["Description"], "Collection of System Dump Entries"); | |
| EXPECT_EQ(resp["Members"].size(), resp["Members@odata.count"]); | |
| EXPECT_EQ(resp["Members"].size(), 1); | |
| auto& member = resp["Members"][0]; | |
| EXPECT_EQ(member["AdditionalDataSizeBytes"], BMC_DUMP_SIZE); | |
| EXPECT_EQ(member["EntryType"], "Event"); | |
| EXPECT_EQ(member["Name"], "System Dump Entry"); | |
| EXPECT_EQ(member["OEMDiagnosticDataType"], "System"); | |
| } | |
| TEST_F(SnapshotFixture, HealthMonitorLogServiceGetSuccess) { | |
| // Mock the D-Bus call to getSubTreePaths to simulate a single-host | |
| // environment where "system" is a valid system name. | |
| KeyType key(ManagedType::kManagedSubtreePaths, | |
| "/xyz/openbmc_project/inventory", 0, | |
| {"xyz.openbmc_project.Inventory.Item.System"}); | |
| MapperGetSubTreePathsResponse mockResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| std::shared_ptr<ValueType> mockValue = | |
| CreateValueType(std::move(mockResponse)); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore(key, mockValue) | |
| .ok()); | |
| const std::string systemName = "system"; | |
| // Call the newly created handler function | |
| handleHealthMonitorLogServiceGet(app_, CreateRequest(), share_async_resp_, | |
| systemName); | |
| RunIoUntilDone(); | |
| // Verify the response | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| const std::string odataId = | |
| "/redfish/v1/Systems/system/LogServices/HealthMonitor"; | |
| EXPECT_EQ(json["@odata.id"], odataId); | |
| EXPECT_EQ(json["@odata.type"], "#LogService.v1_1_0.LogService"); | |
| EXPECT_EQ(json["Name"], "Health Monitor Log Service"); | |
| EXPECT_EQ(json["Description"], "Health Monitor Log Service"); | |
| EXPECT_EQ(json["Id"], "HealthMonitor"); | |
| EXPECT_EQ(json["Entries"]["@odata.id"], odataId + "/Entries"); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| } | |
| TEST_F(SnapshotFixture, HealthMonitorLogEntryCollectionGetSuccess) { | |
| // Clean up the singleton for other tests. | |
| HealthStatusCache::getInstance().clear(); | |
| // Part 1: Populate the HealthStatusCache with a mock entry. | |
| // This requires mocking the D-Bus objects that the HealthStatusCache | |
| // constructor and getHealthAttributes() would normally read. | |
| // Mock the initial discovery of health monitor configurations. | |
| const std::string healthMonitorPath = | |
| "/xyz/openbmc_project/inventory/system/board/CPU_0/Temp"; | |
| const MapperGetSubTreePathsResponse mockSubTreePaths = {healthMonitorPath}; | |
| KeyType subTreeKey(ManagedType::kManagedSubtreePaths, | |
| "/xyz/openbmc_project/inventory", 0, | |
| {"xyz.openbmc_project.Configuration.HealthMonitor"}); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| subTreeKey, CreateValueType(std::move(mockSubTreePaths))) | |
| .ok()); | |
| // Mock the properties of the health monitor configuration itself. | |
| DBusPropertiesMap configProperties{ | |
| {"Name", DbusVariantType("Temp")}, | |
| {"Severity", DbusVariantType("Critical")}, | |
| {"FailureMessageId", | |
| DbusVariantType("ResourceEvent.1.0.ResourceErrorsDetected")}, | |
| {"OkMessageId", | |
| DbusVariantType("ResourceEvent.1.0.ResourceErrorsCorrected")}, | |
| {"MessageArgs", | |
| DbusVariantType(std::vector<std::string>{"CPU_0", "Temperature"})}, | |
| {"Resolution", DbusVariantType("None")}, | |
| {"RelatedEntityName", DbusVariantType("CPU_0")}, | |
| {"RelatedEntityType", DbusVariantType("Processor")}, | |
| }; | |
| KeyType configKey(ManagedType::kManagedPropertyMap, | |
| "xyz.openbmc_project.EntityManager", | |
| sdbusplus::message::object_path(healthMonitorPath), | |
| "xyz.openbmc_project.Configuration.HealthMonitor"); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| configKey, CreateValueType(std::move(configProperties))) | |
| .ok()); | |
| // Mock the current health status (unhealthy in this case). | |
| DBusPropertiesMap statusProperties{ | |
| {"healthy", DbusVariantType(false)}, | |
| {"toggle_count", DbusVariantType(uint64_t{1})}}; | |
| sdbusplus::message::object_path statusPath( | |
| "/xyz/openbmc_project/HealthMonitor/Temp"); | |
| KeyType statusKey(ManagedType::kManagedPropertyMap, | |
| "xyz.openbmc_project.HealthMonitor", statusPath, | |
| "xyz.openbmc_project.HealthMonitor.GetStatus"); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| statusKey, CreateValueType(std::move(statusProperties))) | |
| .ok()); | |
| // Trigger the cache to populate itself with the mock data. | |
| HealthStatusCache::getInstance().getHealthAttributes(healthMonitorPath); | |
| RunIoUntilDone(); // Ensure async D-Bus calls within getHealthAttributes | |
| // complete. | |
| // Part 2: Mock the system path validation. | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, | |
| "/xyz/openbmc_project/inventory", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| // Part 3: Call the handler and verify the response. | |
| const std::string systemName = "system"; | |
| handleHealthMonitorLogEntryCollectionGet(app_, CreateRequest(), | |
| share_async_resp_, systemName); | |
| RunIoUntilDone(); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| const std::string odataId = | |
| "/redfish/v1/Systems/system/LogServices/HealthMonitor/Entries"; | |
| EXPECT_EQ(json["@odata.id"], odataId); | |
| EXPECT_EQ(json["@odata.type"], "#LogEntryCollection.LogEntryCollection"); | |
| EXPECT_EQ(json["Name"], "Health Monitor Log Entries"); | |
| EXPECT_EQ(json["Members@odata.count"], 1); | |
| ASSERT_EQ(json["Members"].size(), 1); | |
| // Verify the content of the log entry. | |
| const nlohmann::json& logEntry = json["Members"][0]; | |
| EXPECT_EQ(logEntry["Id"], "0"); | |
| EXPECT_EQ(logEntry["Severity"], "Critical"); | |
| EXPECT_EQ(logEntry["Resolved"], false); | |
| EXPECT_EQ(logEntry["MessageId"], "ResourceEvent.1.0.ResourceErrorsDetected"); | |
| EXPECT_EQ( | |
| logEntry["Message"], | |
| "The resource property CPU_0 has detected errors of type Temperature"); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| // Clean up the singleton for other tests. | |
| HealthStatusCache::getInstance().clear(); | |
| } | |
| TEST_F(SnapshotFixture, HealthMonitorLogEntryGetSuccess) { | |
| // FIX: Clear both the cache and the underlying mock D-Bus store. | |
| HealthStatusCache::getInstance().clear(); | |
| // Part 1: Populate the HealthStatusCache with a mock entry. | |
| const std::string healthMonitorPath = | |
| "/xyz/openbmc_project/inventory/system/board/CPU_0/Temp"; | |
| const MapperGetSubTreePathsResponse mockSubTreePaths = {healthMonitorPath}; | |
| KeyType subTreeKey(ManagedType::kManagedSubtreePaths, | |
| "/xyz/openbmc_project/inventory", 0, | |
| {"xyz.openbmc_project.Configuration.HealthMonitor"}); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| subTreeKey, CreateValueType(std::move(mockSubTreePaths))) | |
| .ok()); | |
| // In HealthMonitorLogEntryGetSuccess test case | |
| DBusPropertiesMap configProperties{ | |
| {"Name", DbusVariantType("Temp")}, | |
| {"Severity", DbusVariantType("Critical")}, | |
| {"FailureMessageId", | |
| DbusVariantType("ResourceEvent.1.0.ResourceErrorsDetected")}, | |
| {"OkMessageId", | |
| DbusVariantType("ResourceEvent.1.0.ResourceErrorsCorrected")}, | |
| {"MessageArgs", | |
| DbusVariantType(std::vector<std::string>{"CPU_0", "Temperature"})}, | |
| {"Resolution", DbusVariantType("None")}, | |
| {"RelatedEntityName", DbusVariantType("CPU_0")}, | |
| {"RelatedEntityType", DbusVariantType("Processor")}, | |
| }; | |
| KeyType configKey(ManagedType::kManagedPropertyMap, | |
| "xyz.openbmc_project.EntityManager", | |
| sdbusplus::message::object_path(healthMonitorPath), | |
| "xyz.openbmc_project.Configuration.HealthMonitor"); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| configKey, CreateValueType(std::move(configProperties))) | |
| .ok()); | |
| DBusPropertiesMap statusProperties{ | |
| {"healthy", DbusVariantType(false)}, | |
| {"toggle_count", DbusVariantType(uint64_t{1})}}; | |
| sdbusplus::message::object_path statusPath( | |
| "/xyz/openbmc_project/HealthMonitor/Temp"); | |
| KeyType statusKey(ManagedType::kManagedPropertyMap, | |
| "xyz.openbmc_project.HealthMonitor", statusPath, | |
| "xyz.openbmc_project.HealthMonitor.GetStatus"); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| statusKey, CreateValueType(std::move(statusProperties))) | |
| .ok()); | |
| HealthStatusCache::getInstance().getHealthAttributes(healthMonitorPath); | |
| RunIoUntilDone(); | |
| // Part 2: Mock the system path validation. | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, | |
| "/xyz/openbmc_project/inventory", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| // Part 3: Call the handler for a single entry and verify the response. | |
| const std::string systemName = "system"; | |
| const std::string entryId = "0"; // Request the first log entry | |
| handleHealthMonitorLogEntryGet(app_, CreateRequest(), share_async_resp_, | |
| systemName, entryId); | |
| RunIoUntilDone(); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| // Verify it's a single LogEntry object, not a collection | |
| EXPECT_EQ(json["@odata.type"], "#LogEntry.v1_9_0.LogEntry"); | |
| EXPECT_EQ(json["Id"], "0"); | |
| EXPECT_EQ(json["Severity"], "Critical"); | |
| EXPECT_EQ(json["Resolved"], false); | |
| EXPECT_EQ(json["MessageId"], "ResourceEvent.1.0.ResourceErrorsDetected"); | |
| EXPECT_EQ( | |
| json["Message"], | |
| "The resource property CPU_0 has detected errors of type Temperature"); | |
| // Ensure it's not a collection | |
| EXPECT_FALSE(json.contains("Members")); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| HealthStatusCache::getInstance().clear(); | |
| } | |
| TEST_F(SnapshotFixture, GetSystemLogServicesCollectionSuccess) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| KeyType postCodeKey( | |
| ManagedType::kManagedSubtreePaths, "/", 0, | |
| std::vector<std::string>{"xyz.openbmc_project.State.Boot.PostCode"}); | |
| MapperGetSubTreePathsResponse mockPostCodeResponse = { | |
| "/xyz/openbmc_project/State/Boot/PostCode0"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| postCodeKey, CreateValueType(mockPostCodeResponse)) | |
| .ok()); | |
| handleSystemLogServicesCollectionGet(app_, CreateRequest(), share_async_resp_, | |
| "system"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["@odata.id"], "/redfish/v1/Systems/system/LogServices"); | |
| EXPECT_EQ(json["@odata.type"], "#LogServiceCollection.LogServiceCollection"); | |
| EXPECT_EQ(json["Name"], "System Log Services Collection"); | |
| bool foundCrashdump = false; | |
| bool foundPostCodes = false; | |
| for (const auto& member : json["Members"]) { | |
| if (member["@odata.id"] == | |
| "/redfish/v1/Systems/system/LogServices/Crashdump") { | |
| foundCrashdump = true; | |
| } | |
| if (member["@odata.id"] == | |
| "/redfish/v1/Systems/system/LogServices/PostCodes") { | |
| foundPostCodes = true; | |
| } | |
| } | |
| EXPECT_TRUE(foundCrashdump); | |
| EXPECT_TRUE(foundPostCodes); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpServiceSuccess) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| handleGetCrashdumpService(app_, CreateRequest(), share_async_resp_, "system"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| json.erase("DateTime"); | |
| json.erase("DateTimeLocalOffset"); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/Crashdump", | |
| "@odata.type": "#LogService.v1_2_0.LogService", | |
| "Actions": { | |
| "#LogService.ClearLog": { | |
| "target": "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog" | |
| }, | |
| "#LogService.CollectDiagnosticData": { | |
| "target": "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData" | |
| } | |
| }, | |
| "Description": "Crashdump Service", | |
| "Entries": { | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries" | |
| }, | |
| "Id": "Crashdump", | |
| "Name": "Crashdump Service", | |
| "OverWritePolicy": "WrapsWhenFull" | |
| })"); | |
| EXPECT_EQ(json, expectedJson); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpServiceFailure) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system1", | |
| "/xyz/openbmc_project/inventory/system/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| handleGetCrashdumpService(app_, CreateRequest(), share_async_resp_, "system"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::not_found); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryCollectionSuccess) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| const std::vector<std::string> crashInterfaces = {"com.intel.crashdump"}; | |
| KeyType crashdumpKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| crashInterfaces); | |
| std::vector<std::string> mockCrashdumpPaths = {"/com/intel/crashdump/log1"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| crashdumpKey, CreateValueType(mockCrashdumpPaths)) | |
| .ok()); | |
| DBusPropertiesMap crashdumpProperties{ | |
| {"Filename", DbusVariantType("crash_log_file")}, | |
| {"Timestamp", DbusVariantType("2026-07-31T22:00:00Z")}, | |
| {"Log", DbusVariantType("crash_filepath")}, | |
| }; | |
| KeyType crashPropertiesKey( | |
| ManagedType::kManagedPropertyMap, "com.intel.crashdump", | |
| sdbusplus::message::object_path("/com/intel/crashdump/log1"), | |
| "com.intel.crashdump"); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| crashPropertiesKey, | |
| CreateValueType(std::move(crashdumpProperties))) | |
| .ok()); | |
| handleGetCrashdumpEntryCollection(app_, CreateRequest(), share_async_resp_, | |
| "system"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries", | |
| "@odata.type": "#LogEntryCollection.LogEntryCollection", | |
| "Description": "Collection of Crashdump Entries", | |
| "Members": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/intel_log1", | |
| "@odata.type": "#LogEntry.v1_9_0.LogEntry", | |
| "AdditionalDataURI": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/intel_log1/crash_log_file", | |
| "Created": "2026-07-31T22:00:00Z", | |
| "DiagnosticDataType": "OEM", | |
| "EntryType": "Oem", | |
| "Id": "log1", | |
| "Name": "CPU Crashdump", | |
| "OEMDiagnosticDataType": "PECICrashdump" | |
| } | |
| ], | |
| "Members@odata.count": 1, | |
| "Name": "Crashdump Entries" | |
| })"); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue, expectedJson); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntrySuccess) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| DBusPropertiesMap crashdumpProperties{ | |
| {"Filename", DbusVariantType("crash_log_file")}, | |
| {"Timestamp", DbusVariantType("2026-07-31T22:00:00Z")}, | |
| {"Log", DbusVariantType("crash_filepath")}, | |
| }; | |
| KeyType crashPropertiesKey( | |
| ManagedType::kManagedPropertyMap, "com.intel.crashdump", | |
| sdbusplus::message::object_path("/com/intel/crashdump/log1"), | |
| "com.intel.crashdump"); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| crashPropertiesKey, | |
| CreateValueType(std::move(crashdumpProperties))) | |
| .ok()); | |
| handleGetCrashdumpEntry(app_, CreateRequest(), share_async_resp_, "system", | |
| "intel_log1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/intel_log1", | |
| "@odata.type": "#LogEntry.v1_9_0.LogEntry", | |
| "AdditionalDataURI": "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/intel_log1/crash_log_file", | |
| "Created": "2026-07-31T22:00:00Z", | |
| "DiagnosticDataType": "OEM", | |
| "EntryType": "Oem", | |
| "Id": "log1", | |
| "Name": "CPU Crashdump", | |
| "OEMDiagnosticDataType": "PECICrashdump" | |
| })"); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue, expectedJson); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryFaultLogSuccess) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system1", | |
| "/xyz/openbmc_project/inventory/system/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/0/entry/2"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")))}}), | |
| std::make_pair("xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(0)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("2")))}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry))) | |
| .ok()); | |
| std::string tempDirPath = "/tmp/test_dumpmanager_fl"; | |
| std::string targetDir = | |
| tempDirPath + "/var/run/pldm/event/repo/host0/Crashdump/"; | |
| std::filesystem::create_directories(targetDir); | |
| { | |
| std::ofstream ofs(targetDir + "2", std::ios::binary); | |
| } | |
| handleGetCrashdumpEntry(app_, CreateRequest(), share_async_resp_, "system1", | |
| "dumpmanager_system1_2", tempDirPath); | |
| RunIoUntilDone(); | |
| std::filesystem::remove_all(tempDirPath); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system1/LogServices/Crashdump/Entries/dumpmanager_system1_2", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticData": "", | |
| "DiagnosticDataType": "CPER", | |
| "EntryType": "Oem", | |
| "Id": "2", | |
| "Name": "Host Crashdump Entry" | |
| })"); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue, expectedJson); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryMultiHostSuccess) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system1", | |
| "/xyz/openbmc_project/inventory/system/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/0/entry/1"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")))}}), | |
| std::make_pair("xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(0)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("1")))}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry))) | |
| .ok()); | |
| std::string tempDirPath = "/tmp/test_dumpmanager_mh1"; | |
| std::string targetDir = | |
| tempDirPath + "/var/run/pldm/event/repo/host0/Crashdump/"; | |
| std::filesystem::create_directories(targetDir); | |
| { | |
| std::ofstream ofs(targetDir + "1", std::ios::binary); | |
| } | |
| handleGetCrashdumpEntry(app_, CreateRequest(), share_async_resp_, "system1", | |
| "dumpmanager_system1_1", tempDirPath); | |
| RunIoUntilDone(); | |
| std::filesystem::remove_all(tempDirPath); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system1/LogServices/Crashdump/Entries/dumpmanager_system1_1", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticData": "", | |
| "DiagnosticDataType": "CPER", | |
| "EntryType": "Oem", | |
| "Id": "1", | |
| "Name": "Host Crashdump Entry" | |
| })"); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue, expectedJson); | |
| } | |
| TEST(CrashdumpFolderPathTest, CrashdumpFolderPathFromSystemName) { | |
| EXPECT_EQ(crashdumpFolderPathFromSystemName("system"), std::nullopt); | |
| EXPECT_EQ(crashdumpFolderPathFromSystemName("system1"), | |
| "/var/run/pldm/event/repo/host0/Crashdump/"); | |
| EXPECT_EQ(crashdumpFolderPathFromSystemName("system2"), | |
| "/var/run/pldm/event/repo/host1/Crashdump/"); | |
| EXPECT_EQ(crashdumpFolderPathFromSystemName("unknown"), std::nullopt); | |
| EXPECT_EQ(crashdumpFolderPathFromSystemName("system1", "/tmp/test"), | |
| "/tmp/test/var/run/pldm/event/repo/host0/Crashdump/"); | |
| EXPECT_EQ(crashdumpFolderPathFromSystemName("system2", "/tmp/test"), | |
| "/tmp/test/var/run/pldm/event/repo/host1/Crashdump/"); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryMultiHostWithFileSuccess) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system1", | |
| "/xyz/openbmc_project/inventory/system/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/0/entry/1"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")))}}), | |
| std::make_pair("xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(0)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("log1.bin")))}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry))) | |
| .ok()); | |
| std::string tempDirPath = "/tmp/crashdump_test_dir"; | |
| std::string targetDir = | |
| tempDirPath + "/var/run/pldm/event/repo/host0/Crashdump/"; | |
| std::filesystem::create_directories(targetDir); | |
| std::string filePath = targetDir + "log1.bin"; | |
| { | |
| std::ofstream ofs(filePath, std::ios::binary); | |
| // Crashdump files on the BMC are already base64 encoded. | |
| ofs << "dGVzdF9jcmFzaGR1bXBfY29udGVudA=="; | |
| } | |
| handleGetCrashdumpEntry(app_, CreateRequest(), share_async_resp_, "system1", | |
| "dumpmanager_system1_1", tempDirPath); | |
| RunIoUntilDone(); | |
| std::filesystem::remove_all(tempDirPath); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["DiagnosticData"], | |
| "dGVzdF9jcmFzaGR1bXBfY29udGVudA=="); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryCollectionMultiHostSuccess) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system1", | |
| "/xyz/openbmc_project/inventory/system/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| const std::vector<std::string> crashInterfaces = {"com.intel.crashdump"}; | |
| KeyType crashdumpKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| crashInterfaces); | |
| MapperGetSubTreePathsResponse emptyCrashdumpPaths = {}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| crashdumpKey, CreateValueType(emptyCrashdumpPaths)) | |
| .ok()); | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/0/entry/1"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")))}}), | |
| std::make_pair("xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(0)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("1")))}})}})}, | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/entry/2"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")))}}), | |
| std::make_pair("xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(0)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("2")))}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry))) | |
| .ok()); | |
| std::string tempDirPath = "/tmp/test_dumpmanager_mh2"; | |
| std::string targetDir = | |
| tempDirPath + "/var/run/pldm/event/repo/host0/Crashdump/"; | |
| std::filesystem::create_directories(targetDir); | |
| { | |
| std::ofstream ofs(targetDir + "1", std::ios::binary); | |
| } | |
| { | |
| std::ofstream ofs(targetDir + "2", std::ios::binary); | |
| } | |
| handleGetCrashdumpEntryCollection(app_, CreateRequest(), share_async_resp_, | |
| "system1", tempDirPath); | |
| RunIoUntilDone(); | |
| std::filesystem::remove_all(tempDirPath); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system1/LogServices/Crashdump/Entries", | |
| "@odata.type": "#LogEntryCollection.LogEntryCollection", | |
| "Description": "Collection of Crashdump Entries", | |
| "Members": [ | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system1/LogServices/Crashdump/Entries/dumpmanager_system1_1", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticData": "", | |
| "DiagnosticDataType": "CPER", | |
| "EntryType": "Oem", | |
| "Id": "1", | |
| "Name": "Host Crashdump Entry" | |
| }, | |
| { | |
| "@odata.id": "/redfish/v1/Systems/system1/LogServices/Crashdump/Entries/dumpmanager_2", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "Created": "1970-01-01T00:00:00.000000+00:00", | |
| "DiagnosticData": "", | |
| "DiagnosticDataType": "CPER", | |
| "EntryType": "Oem", | |
| "Id": "2", | |
| "Name": "Host Crashdump Entry" | |
| } | |
| ], | |
| "Members@odata.count": 2, | |
| "Name": "Crashdump Entries" | |
| })"); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue, expectedJson); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryMissingPropertiesInternalError) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/entry/1"), | |
| DBusInteracesMap{{std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair("Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair("PrimaryLogId", DbusVariantType(std::string( | |
| "")) /* Empty */)}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry))) | |
| .ok()); | |
| handleGetCrashdumpEntry(app_, CreateRequest(), share_async_resp_, "system", | |
| "dumpmanager_1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::internal_server_error); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryShortTypeCrashdumpSuccess) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system1", | |
| "/xyz/openbmc_project/inventory/system/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/0/entry/3"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(1000000)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", DbusVariantType(std::string("Crashdump"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("3")))}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry))) | |
| .ok()); | |
| std::string tempDirPath = "/tmp/test_dumpmanager_dir4"; | |
| std::string hostDir = | |
| tempDirPath + "/var/run/pldm/event/repo/host0/Crashdump/"; | |
| std::filesystem::create_directories(hostDir); | |
| { | |
| std::ofstream ofs(hostDir + "3", std::ios::binary); | |
| ofs << "dummy"; | |
| } | |
| handleGetCrashdumpEntry(app_, CreateRequest(), share_async_resp_, "system1", | |
| "dumpmanager_system1_3", tempDirPath); | |
| RunIoUntilDone(); | |
| std::filesystem::remove_all(tempDirPath); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["Id"], "3"); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryMissingTimestampInternalError) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/entry/1"), | |
| DBusInteracesMap{{std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair("Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair("PrimaryLogId", DbusVariantType(std::string( | |
| "valid_id")))}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry))) | |
| .ok()); | |
| handleGetCrashdumpEntry(app_, CreateRequest(), share_async_resp_, "system", | |
| "dumpmanager_1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::internal_server_error); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryNonCrashdumpTypeNotFound) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/entry/5"), | |
| DBusInteracesMap{ | |
| {std::make_pair("xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(100)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair("Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.CPER"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("5")))}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry))) | |
| .ok()); | |
| handleGetCrashdumpEntry(app_, CreateRequest(), share_async_resp_, "system", | |
| "dumpmanager_5"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::not_found); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryWithFilePayloadSuccess) { | |
| std::string tempDirPath = "/tmp/test_dumpmanager_dir3"; | |
| std::string hostDir = | |
| tempDirPath + "/var/run/pldm/event/repo/host0/Crashdump/"; | |
| std::filesystem::create_directories(hostDir); | |
| { | |
| std::ofstream ofs(hostDir + "10", std::ios::binary); | |
| // Crashdump files on the BMC are already base64 encoded. | |
| ofs << "Q1JBU0hEVU1QX1RFU1RfUEFZTE9BRA=="; | |
| } | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system1", | |
| "/xyz/openbmc_project/inventory/system/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/0/entry/10"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(500000)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("10")))}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry))) | |
| .ok()); | |
| handleGetCrashdumpEntry(app_, CreateRequest(), share_async_resp_, "system1", | |
| "dumpmanager_system1_10", tempDirPath); | |
| RunIoUntilDone(); | |
| std::filesystem::remove_all(tempDirPath); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json expectedJson = nlohmann::json::parse(R"({ | |
| "@odata.id": "/redfish/v1/Systems/system1/LogServices/Crashdump/Entries/dumpmanager_system1_10", | |
| "@odata.type": "#LogEntry.v1_11_0.LogEntry", | |
| "Created": "1970-01-01T00:00:00.500000+00:00", | |
| "DiagnosticData": "Q1JBU0hEVU1QX1RFU1RfUEFZTE9BRA==", | |
| "DiagnosticDataType": "CPER", | |
| "EntryType": "Oem", | |
| "Id": "10", | |
| "Name": "Host Crashdump Entry" | |
| })"); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue, expectedJson); | |
| } | |
| TEST_F(SnapshotFixture, | |
| GetCrashdumpEntryCollectionPrimaryCrashdumpSuccessAndEmptyPath) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system1", | |
| "/xyz/openbmc_project/inventory/system/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| const std::vector<std::string> crashInterfaces = {"com.intel.crashdump"}; | |
| KeyType crashdumpKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| crashInterfaces); | |
| MapperGetSubTreePathsResponse crashdumpPaths = { | |
| "/", // Empty filename -> skipped | |
| "/com/intel/crashdump/log1" // Valid primary crashdump | |
| }; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| crashdumpKey, CreateValueType(crashdumpPaths)) | |
| .ok()); | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/0/entry/2"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")))}}), | |
| std::make_pair("xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(0)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("2")))}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, CreateMockFaultLogCPEREntry()) | |
| .ok()); | |
| std::string tempDirPath = "/tmp/test_dumpmanager_dir"; | |
| std::string hostDir = | |
| tempDirPath + "/var/run/pldm/event/repo/host0/Crashdump/"; | |
| std::filesystem::create_directories(hostDir); | |
| { | |
| std::ofstream ofs(hostDir + "2", std::ios::binary); | |
| ofs << "dummy"; | |
| } | |
| handleGetCrashdumpEntryCollection(app_, CreateRequest(), share_async_resp_, | |
| "system1", tempDirPath); | |
| RunIoUntilDone(); | |
| std::filesystem::remove_all(tempDirPath); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["Members@odata.count"], 2); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryCollectionPrimaryCrashdumpDbusError) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system1", | |
| "/xyz/openbmc_project/inventory/system/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| const std::vector<std::string> crashInterfaces = {"com.intel.crashdump"}; | |
| KeyType crashdumpKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| crashInterfaces); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| crashdumpKey, | |
| CreateErrorValueType(MapperGetSubTreePathsResponse{}, | |
| boost::system::errc::make_error_code( | |
| boost::system::errc::io_error))) | |
| .ok()); | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/0/entry/2"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")))}}), | |
| std::make_pair("xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(0)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("2")))}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, CreateMockFaultLogCPEREntry()) | |
| .ok()); | |
| std::string tempDirPath = "/tmp/test_dumpmanager_dir2"; | |
| std::string hostDir = | |
| tempDirPath + "/var/run/pldm/event/repo/host0/Crashdump/"; | |
| std::filesystem::create_directories(hostDir); | |
| { | |
| std::ofstream ofs(hostDir + "2", std::ios::binary); | |
| ofs << "dummy"; | |
| } | |
| handleGetCrashdumpEntryCollection(app_, CreateRequest(), share_async_resp_, | |
| "system1", tempDirPath); | |
| RunIoUntilDone(); | |
| std::filesystem::remove_all(tempDirPath); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryCollectionDumpManagerDbusError) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| const std::vector<std::string> crashInterfaces = {"com.intel.crashdump"}; | |
| KeyType crashdumpKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| crashInterfaces); | |
| MapperGetSubTreePathsResponse emptyCrashdumpPaths = {}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| crashdumpKey, CreateValueType(emptyCrashdumpPaths)) | |
| .ok()); | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, | |
| CreateErrorValueType(ManagedObjectType{}, | |
| boost::system::errc::make_error_code( | |
| boost::system::errc::io_error))) | |
| .ok()); | |
| handleGetCrashdumpEntryCollection(app_, CreateRequest(), share_async_resp_, | |
| "system"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["Members@odata.count"], 0); | |
| } | |
| TEST_F( | |
| SnapshotFixture, | |
| GetCrashdumpEntryCollectionDumpManagerSkipNonMatchingPrefixAndEmptyFilename) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| const std::vector<std::string> crashInterfaces = {"com.intel.crashdump"}; | |
| KeyType crashdumpKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| crashInterfaces); | |
| MapperGetSubTreePathsResponse emptyCrashdumpPaths = {}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| crashdumpKey, CreateValueType(emptyCrashdumpPaths)) | |
| .ok()); | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair(sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/other/entry/1"), | |
| DBusInteracesMap{}), | |
| std::make_pair(sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/entry/"), | |
| DBusInteracesMap{})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry))) | |
| .ok()); | |
| handleGetCrashdumpEntryCollection(app_, CreateRequest(), share_async_resp_, | |
| "system"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["Members@odata.count"], 0); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryCollectionInvalidSystemName) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| handleGetCrashdumpEntryCollection(app_, CreateRequest(), share_async_resp_, | |
| "invalid_system"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::not_found); | |
| handleGetCrashdumpEntry(app_, CreateRequest(), share_async_resp_, | |
| "invalid_system", "dumpmanager_1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::not_found); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryPropertyTypeMismatchInternalError) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/entry/20"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{ | |
| {std::make_pair("Elapsed", | |
| DbusVariantType(std::string("invalid"))), | |
| std::make_pair("OtherProp", | |
| DbusVariantType(uint64_t(100)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair("Type", DbusVariantType(uint64_t(123))), | |
| std::make_pair("PrimaryLogId", | |
| DbusVariantType(uint64_t(456))), | |
| std::make_pair("OtherProp", DbusVariantType(std::string( | |
| "val")))}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry))) | |
| .ok()); | |
| handleGetCrashdumpEntry(app_, CreateRequest(), share_async_resp_, "system", | |
| "dumpmanager_20"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::not_found); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryPrettyNameSuccess) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system1", | |
| "/xyz/openbmc_project/inventory/system/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/entry/8"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(1000000)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair("PrimaryLogId", | |
| DbusVariantType(std::string("8"))), | |
| std::make_pair("PrettyName", | |
| DbusVariantType(std::string( | |
| "Component 1 Crashdump")))}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry))) | |
| .ok()); | |
| std::string tempDirPath = "/tmp/test_dumpmanager_pn"; | |
| std::string targetDir = | |
| tempDirPath + "/var/run/pldm/event/repo/host0/Crashdump/"; | |
| std::filesystem::create_directories(targetDir); | |
| { | |
| std::ofstream ofs(targetDir + "8", std::ios::binary); | |
| } | |
| handleGetCrashdumpEntry(app_, CreateRequest(), share_async_resp_, "system1", | |
| "dumpmanager_8", tempDirPath); | |
| RunIoUntilDone(); | |
| std::filesystem::remove_all(tempDirPath); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["Name"], "Component 1 Crashdump"); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryMultiHostSystemAgnosticSuccess) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system1", | |
| "/xyz/openbmc_project/inventory/system/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| // Entry at system-agnostic path /xyz/openbmc_project/dump/faultlog/entry/9 | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/entry/9"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(2000000)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair("PrimaryLogId", | |
| DbusVariantType(std::string("9"))), | |
| std::make_pair("PrettyName", | |
| DbusVariantType(std::string( | |
| "Component 2 Crashdump")))}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry))) | |
| .ok()); | |
| std::string tempDirPath = "/tmp/test_dumpmanager_sa"; | |
| std::string targetDir = | |
| tempDirPath + "/var/run/pldm/event/repo/host0/Crashdump/"; | |
| std::filesystem::create_directories(targetDir); | |
| { | |
| std::ofstream ofs(targetDir + "9", std::ios::binary); | |
| } | |
| handleGetCrashdumpEntry(app_, CreateRequest(), share_async_resp_, "system1", | |
| "dumpmanager_9", tempDirPath); | |
| RunIoUntilDone(); | |
| std::filesystem::remove_all(tempDirPath); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["Id"], "9"); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["Name"], "Component 2 Crashdump"); | |
| } | |
| TEST_F(SnapshotFixture, | |
| GetCrashdumpEntryCollectionMultiHostIncludesSystemAgnosticAndSpecific) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system1", | |
| "/xyz/openbmc_project/inventory/system/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| const std::vector<std::string> crashInterfaces = {"com.intel.crashdump"}; | |
| KeyType crashdumpKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| crashInterfaces); | |
| MapperGetSubTreePathsResponse emptyCrashdumpPaths = {}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| crashdumpKey, CreateValueType(emptyCrashdumpPaths)) | |
| .ok()); | |
| // Entry 1: System-specific for system1 | |
| // (/xyz/openbmc_project/dump/faultlog/0/entry/1) Entry 2: System-agnostic | |
| // (/xyz/openbmc_project/dump/faultlog/entry/2) | |
| ManagedObjectType mockFaultLogEntries{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/0/entry/1"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(100)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair( | |
| "PrimaryLogId", | |
| DbusVariantType(std::string("1")))}})}}), | |
| std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/entry/2"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(200)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair("PrimaryLogId", | |
| DbusVariantType(std::string("2"))), | |
| std::make_pair( | |
| "PrettyName", | |
| DbusVariantType(std::string( | |
| "Component Shared Crashdump")))}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntries))) | |
| .ok()); | |
| std::string tempDirPath = "/tmp/test_dumpmanager_mh3"; | |
| std::string targetDir = | |
| tempDirPath + "/var/run/pldm/event/repo/host0/Crashdump/"; | |
| std::filesystem::create_directories(targetDir); | |
| { | |
| std::ofstream ofs(targetDir + "1", std::ios::binary); | |
| } | |
| { | |
| std::ofstream ofs(targetDir + "2", std::ios::binary); | |
| } | |
| handleGetCrashdumpEntryCollection(app_, CreateRequest(), share_async_resp_, | |
| "system1", tempDirPath); | |
| RunIoUntilDone(); | |
| std::filesystem::remove_all(tempDirPath); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["Members@odata.count"], 2); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["Members"][0]["Id"], "1"); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["Members"][1]["Id"], "2"); | |
| } | |
| TEST_F(SnapshotFixture, GetCrashdumpEntryLogDbusPropertyOverrideSuccess) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system1", | |
| "/xyz/openbmc_project/inventory/system/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| ManagedObjectType mockFaultLogEntry{ | |
| {std::make_pair( | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/dump/faultlog/0/entry/15"), | |
| DBusInteracesMap{ | |
| {std::make_pair( | |
| "xyz.openbmc_project.Common.Progress", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Status", DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common.Progress." | |
| "OperationStatus.Completed")))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Time.EpochTime", | |
| DBusPropertiesMap{{std::make_pair( | |
| "Elapsed", DbusVariantType(uint64_t(3000000)))}}), | |
| std::make_pair( | |
| "xyz.openbmc_project.Dump.Entry.FaultLog", | |
| DBusPropertiesMap{ | |
| {std::make_pair( | |
| "Type", | |
| DbusVariantType(std::string( | |
| "xyz.openbmc_project.Common." | |
| "FaultLogType.FaultLogTypes.Crashdump"))), | |
| std::make_pair("PrimaryLogId", | |
| DbusVariantType(std::string("15"))), | |
| std::make_pair( | |
| "Log", | |
| DbusVariantType(std::string( | |
| "/custom/path/to/crashdump15.bin")))}})}})}, | |
| }; | |
| KeyType dumpKey(ManagedType::kManagedObject, | |
| "xyz.openbmc_project.Dump.Manager", | |
| sdbusplus::message::object_path("/xyz/openbmc_project/dump")); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| dumpKey, managedStore::MockManagedStoreTest::CreateValueType( | |
| std::move(mockFaultLogEntry))) | |
| .ok()); | |
| std::string tempDirPath = "/tmp/test_dumpmanager_log_prop"; | |
| std::string customFileDir = tempDirPath + "/custom/path/to/"; | |
| std::filesystem::create_directories(customFileDir); | |
| { | |
| std::ofstream ofs(customFileDir + "crashdump15.bin", std::ios::binary); | |
| // Crashdump files on the BMC are already base64 encoded. | |
| ofs << "Y3VzdG9tX2xvZ19wcm9wZXJ0eV9jb250ZW50"; | |
| } | |
| handleGetCrashdumpEntry(app_, CreateRequest(), share_async_resp_, "system1", | |
| "dumpmanager_system1_15", tempDirPath); | |
| RunIoUntilDone(); | |
| std::filesystem::remove_all(tempDirPath); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| EXPECT_EQ(share_async_resp_->res.jsonValue["DiagnosticData"], | |
| "Y3VzdG9tX2xvZ19wcm9wZXJ0eV9jb250ZW50"); | |
| } | |
| TEST_F(SnapshotFixture, PostCodesLogServiceGetSuccess_SingleHost) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| handleGetPostCodesLogService(app_, CreateRequest(), share_async_resp_, | |
| "system"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["@odata.id"], | |
| "/redfish/v1/Systems/system/LogServices/PostCodes"); | |
| EXPECT_EQ(json["@odata.type"], "#LogService.v1_1_0.LogService"); | |
| EXPECT_EQ(json["Id"], "PostCodes"); | |
| EXPECT_EQ(json["Name"], "POST Code Log Service"); | |
| EXPECT_EQ(json["Description"], "POST Code Log Service"); | |
| EXPECT_EQ(json["OverWritePolicy"], "WrapsWhenFull"); | |
| EXPECT_EQ(json["Entries"]["@odata.id"], | |
| "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"); | |
| EXPECT_EQ(json["Actions"]["#LogService.ClearLog"]["target"], | |
| "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/" | |
| "LogService.ClearLog"); | |
| share_async_resp_ = std::make_shared<bmcweb::AsyncResp>(); | |
| handleGetPostCodesLogService(app_, CreateRequest(), share_async_resp_, | |
| "system1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::not_found); | |
| share_async_resp_ = std::make_shared<bmcweb::AsyncResp>(); | |
| handleGetPostCodesLogService(app_, CreateRequest(), share_async_resp_, | |
| "invalid"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::not_found); | |
| } | |
| TEST_F(SnapshotFixture_Platform23, PostCodesLogServiceGetSuccess_MultiHost) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockMultiSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/board/platform23/system1", | |
| "/xyz/openbmc_project/inventory/system/board/platform23/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockMultiSystemResponse)) | |
| .ok()); | |
| handleGetPostCodesLogService(app_, CreateRequest(), share_async_resp_, | |
| "system1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json& json1 = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json1["@odata.id"], | |
| "/redfish/v1/Systems/system1/LogServices/PostCodes"); | |
| EXPECT_EQ(json1["@odata.type"], "#LogService.v1_1_0.LogService"); | |
| EXPECT_EQ(json1["Id"], "PostCodes"); | |
| EXPECT_EQ(json1["Name"], "POST Code Log Service"); | |
| EXPECT_EQ(json1["Entries"]["@odata.id"], | |
| "/redfish/v1/Systems/system1/LogServices/PostCodes/Entries"); | |
| EXPECT_EQ(json1["Actions"]["#LogService.ClearLog"]["target"], | |
| "/redfish/v1/Systems/system1/LogServices/PostCodes/Actions/" | |
| "LogService.ClearLog"); | |
| share_async_resp_ = std::make_shared<bmcweb::AsyncResp>(); | |
| handleGetPostCodesLogService(app_, CreateRequest(), share_async_resp_, | |
| "system2"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json& json2 = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json2["@odata.id"], | |
| "/redfish/v1/Systems/system2/LogServices/PostCodes"); | |
| EXPECT_EQ(json2["Entries"]["@odata.id"], | |
| "/redfish/v1/Systems/system2/LogServices/PostCodes/Entries"); | |
| for (const char* invalidSystem : | |
| {"system0", "system3", "system1abc", "system1/abc", "system", | |
| "system99999999999999999999999999", "foo"}) { | |
| share_async_resp_ = std::make_shared<bmcweb::AsyncResp>(); | |
| handleGetPostCodesLogService(app_, CreateRequest(), share_async_resp_, | |
| invalidSystem); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), | |
| boost::beast::http::status::not_found); | |
| } | |
| } | |
| TEST_F(SnapshotFixture, PostCodesClearPostSuccess_SingleHost) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, An<absl::AnyInvocable<void(const boost::system::error_code&)>&&>(), | |
| "xyz.openbmc_project.State.Boot.PostCode0", | |
| "/xyz/openbmc_project/State/Boot/PostCode0", | |
| "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll")) | |
| .WillOnce([](const auto&, auto&& callback, const auto&, const auto&, | |
| const auto&, | |
| const auto&) { callback(boost::system::error_code()); }); | |
| handlePostPostCodesClear(app_, CreateRequest(), share_async_resp_, "system"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| EXPECT_TRUE( | |
| share_async_resp_->res.jsonValue.contains("@Message.ExtendedInfo")); | |
| } | |
| TEST_F(SnapshotFixture_Platform23, PostCodesClearPostSuccess_MultiHost) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockMultiSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/board/platform23/system1", | |
| "/xyz/openbmc_project/inventory/system/board/platform23/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockMultiSystemResponse)) | |
| .ok()); | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, An<absl::AnyInvocable<void(const boost::system::error_code&)>&&>(), | |
| "xyz.openbmc_project.State.Boot.PostCode1", | |
| "/xyz/openbmc_project/State/Boot/PostCode1", | |
| "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll")) | |
| .WillOnce([](const auto&, auto&& callback, const auto&, const auto&, | |
| const auto&, | |
| const auto&) { callback(boost::system::error_code()); }); | |
| handlePostPostCodesClear(app_, CreateRequest(), share_async_resp_, "system1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| EXPECT_TRUE( | |
| share_async_resp_->res.jsonValue.contains("@Message.ExtendedInfo")); | |
| } | |
| TEST_F(SnapshotFixture, PostCodesEntryCollectionGetSuccess_SingleHost) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| KeyType bootCountKey( | |
| ManagedType::kManagedProperty, "xyz.openbmc_project.State.Boot.PostCode0", | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/State/Boot/PostCode0"), | |
| "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount"); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| bootCountKey, CreateValueType(dbus::utility::DbusVariantType( | |
| static_cast<uint16_t>(1)))) | |
| .ok()); | |
| boost::container::flat_map<uint64_t, | |
| std::tuple<uint64_t, std::vector<uint8_t>>> | |
| postcodes = { | |
| {1000000ULL, {0xb000a915ULL, {0x15, 0xa9, 0x00, 0xb0}}}, | |
| }; | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const boost::container::flat_map< | |
| uint64_t, managedStore::MockSerializedManagedObjectStore:: | |
| PostCodeEntry>&)>&&>(), | |
| "xyz.openbmc_project.State.Boot.PostCode0", | |
| "/xyz/openbmc_project/State/Boot/PostCode0", | |
| "xyz.openbmc_project.State.Boot.PostCode", | |
| "GetPostCodesWithTimeStamp", static_cast<uint16_t>(1))) | |
| .WillOnce([postcodes](const auto&, auto&& callback, const auto&, | |
| const auto&, const auto&, const auto&, uint16_t) { | |
| callback(boost::system::error_code(), postcodes); | |
| }); | |
| handleGetPostCodesEntryCollection(app_, CreateRequest(), share_async_resp_, | |
| "system"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["@odata.id"], | |
| "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"); | |
| EXPECT_EQ(json["@odata.type"], "#LogEntryCollection.LogEntryCollection"); | |
| EXPECT_EQ(json["Members@odata.count"], 1); | |
| ASSERT_EQ(json["Members"].size(), 1); | |
| EXPECT_EQ(json["Members"][0]["@odata.id"], | |
| "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/B1-1"); | |
| EXPECT_EQ(json["Members"][0]["Id"], "B1-1"); | |
| EXPECT_EQ(json["Members"][0]["MessageArgs"], | |
| std::vector<std::string>({"1", "0.0000", "0xb000a915"})); | |
| } | |
| TEST_F(SnapshotFixture, PostCodesEntryCollectionGetSuccess_Pagination) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| KeyType bootCountKey( | |
| ManagedType::kManagedProperty, "xyz.openbmc_project.State.Boot.PostCode0", | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/State/Boot/PostCode0"), | |
| "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount"); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| bootCountKey, CreateValueType(dbus::utility::DbusVariantType( | |
| static_cast<uint16_t>(1)))) | |
| .ok()); | |
| boost::container::flat_map<uint64_t, | |
| std::tuple<uint64_t, std::vector<uint8_t>>> | |
| postcodes = { | |
| {1000000ULL, {0xb000a915ULL, {0x15, 0xa9, 0x00, 0xb0}}}, | |
| {2000000ULL, {0xb000a916ULL, {0x16, 0xa9, 0x00, 0xb0}}}, | |
| {3000000ULL, {0xb000a917ULL, {0x17, 0xa9, 0x00, 0xb0}}}, | |
| }; | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const boost::container::flat_map< | |
| uint64_t, managedStore::MockSerializedManagedObjectStore:: | |
| PostCodeEntry>&)>&&>(), | |
| "xyz.openbmc_project.State.Boot.PostCode0", | |
| "/xyz/openbmc_project/State/Boot/PostCode0", | |
| "xyz.openbmc_project.State.Boot.PostCode", | |
| "GetPostCodesWithTimeStamp", static_cast<uint16_t>(1))) | |
| .WillOnce([postcodes](const auto&, auto&& callback, const auto&, | |
| const auto&, const auto&, const auto&, uint16_t) { | |
| callback(boost::system::error_code(), postcodes); | |
| }); | |
| crow::Request req = CreateRequest(); | |
| req.set_method(boost::beast::http::verb::get); | |
| req.target( | |
| "/redfish/v1/Systems/system/LogServices/PostCodes/" | |
| "Entries?$skip=1&$top=1"); | |
| handleGetPostCodesEntryCollection(app_, req, share_async_resp_, "system"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["@odata.id"], | |
| "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"); | |
| EXPECT_EQ(json["Members@odata.count"], 3); | |
| ASSERT_EQ(json["Members"].size(), 1); | |
| EXPECT_EQ(json["Members"][0]["@odata.id"], | |
| "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/B1-2"); | |
| EXPECT_EQ(json["Members@odata.nextLink"], | |
| "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=2"); | |
| share_async_resp_ = std::make_shared<bmcweb::AsyncResp>(); | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const boost::container::flat_map< | |
| uint64_t, managedStore::MockSerializedManagedObjectStore:: | |
| PostCodeEntry>&)>&&>(), | |
| "xyz.openbmc_project.State.Boot.PostCode0", | |
| "/xyz/openbmc_project/State/Boot/PostCode0", | |
| "xyz.openbmc_project.State.Boot.PostCode", | |
| "GetPostCodesWithTimeStamp", static_cast<uint16_t>(1))) | |
| .WillOnce([postcodes](const auto&, auto&& callback, const auto&, | |
| const auto&, const auto&, const auto&, uint16_t) { | |
| callback(boost::system::error_code(), postcodes); | |
| }); | |
| crow::Request reqSkipAll = CreateRequest(); | |
| reqSkipAll.set_method(boost::beast::http::verb::get); | |
| reqSkipAll.target( | |
| "/redfish/v1/Systems/system/LogServices/PostCodes/" | |
| "Entries?$skip=10&$top=5"); | |
| handleGetPostCodesEntryCollection(app_, reqSkipAll, share_async_resp_, | |
| "system"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json& jsonSkipAll = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(jsonSkipAll["@odata.id"], | |
| "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"); | |
| EXPECT_EQ(jsonSkipAll["Members@odata.count"], 3); | |
| EXPECT_TRUE(jsonSkipAll["Members"].empty()); | |
| EXPECT_FALSE(jsonSkipAll.contains("Members@odata.nextLink")); | |
| // Test multi-boot pagination where (top + skip) <= entryCount for later boots | |
| KeyType multiBootKey( | |
| ManagedType::kManagedProperty, "xyz.openbmc_project.State.Boot.PostCode0", | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/State/Boot/PostCode0"), | |
| "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount"); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| multiBootKey, CreateValueType(dbus::utility::DbusVariantType( | |
| static_cast<uint16_t>(2)))) | |
| .ok()); | |
| share_async_resp_ = std::make_shared<bmcweb::AsyncResp>(); | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const boost::container::flat_map< | |
| uint64_t, managedStore::MockSerializedManagedObjectStore:: | |
| PostCodeEntry>&)>&&>(), | |
| "xyz.openbmc_project.State.Boot.PostCode0", | |
| "/xyz/openbmc_project/State/Boot/PostCode0", | |
| "xyz.openbmc_project.State.Boot.PostCode", | |
| "GetPostCodesWithTimeStamp", static_cast<uint16_t>(1))) | |
| .WillOnce([postcodes](const auto&, auto&& callback, const auto&, | |
| const auto&, const auto&, const auto&, uint16_t) { | |
| callback(boost::system::error_code(), postcodes); | |
| }); | |
| boost::container::flat_map<uint64_t, | |
| std::tuple<uint64_t, std::vector<uint8_t>>> | |
| postcodesBoot2 = { | |
| {4000000ULL, {0xb000a918ULL, {0x18, 0xa9, 0x00, 0xb0}}}, | |
| {5000000ULL, {0xb000a919ULL, {0x19, 0xa9, 0x00, 0xb0}}}, | |
| }; | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const boost::container::flat_map< | |
| uint64_t, managedStore::MockSerializedManagedObjectStore:: | |
| PostCodeEntry>&)>&&>(), | |
| "xyz.openbmc_project.State.Boot.PostCode0", | |
| "/xyz/openbmc_project/State/Boot/PostCode0", | |
| "xyz.openbmc_project.State.Boot.PostCode", | |
| "GetPostCodesWithTimeStamp", static_cast<uint16_t>(2))) | |
| .WillOnce([postcodesBoot2](const auto&, auto&& callback, const auto&, | |
| const auto&, const auto&, const auto&, | |
| uint16_t) { | |
| callback(boost::system::error_code(), postcodesBoot2); | |
| }); | |
| crow::Request reqMultiBoot = CreateRequest(); | |
| reqMultiBoot.set_method(boost::beast::http::verb::get); | |
| reqMultiBoot.target( | |
| "/redfish/v1/Systems/system/LogServices/PostCodes/" | |
| "Entries?$top=1&$skip=0"); | |
| handleGetPostCodesEntryCollection(app_, reqMultiBoot, share_async_resp_, | |
| "system"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json& jsonMultiBoot = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(jsonMultiBoot["@odata.id"], | |
| "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"); | |
| EXPECT_EQ(jsonMultiBoot["Members@odata.count"], 5); | |
| ASSERT_EQ(jsonMultiBoot["Members"].size(), 1); | |
| EXPECT_EQ(jsonMultiBoot["Members"][0]["Id"], "B1-1"); | |
| EXPECT_EQ(jsonMultiBoot["Members@odata.nextLink"], | |
| "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=1"); | |
| } | |
| TEST_F(SnapshotFixture_Platform23, | |
| PostCodesEntryCollectionGetSuccess_MultiHost) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockMultiSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/board/platform23/system1", | |
| "/xyz/openbmc_project/inventory/system/board/platform23/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockMultiSystemResponse)) | |
| .ok()); | |
| KeyType bootCountKey( | |
| ManagedType::kManagedProperty, "xyz.openbmc_project.State.Boot.PostCode1", | |
| sdbusplus::message::object_path( | |
| "/xyz/openbmc_project/State/Boot/PostCode1"), | |
| "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount"); | |
| ASSERT_TRUE( | |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| bootCountKey, CreateValueType(dbus::utility::DbusVariantType( | |
| static_cast<uint16_t>(1)))) | |
| .ok()); | |
| boost::container::flat_map<uint64_t, | |
| std::tuple<uint64_t, std::vector<uint8_t>>> | |
| postcodes = { | |
| {1000000ULL, {0xb000a915ULL, {0x15, 0xa9, 0x00, 0xb0}}}, | |
| }; | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const boost::container::flat_map< | |
| uint64_t, managedStore::MockSerializedManagedObjectStore:: | |
| PostCodeEntry>&)>&&>(), | |
| "xyz.openbmc_project.State.Boot.PostCode1", | |
| "/xyz/openbmc_project/State/Boot/PostCode1", | |
| "xyz.openbmc_project.State.Boot.PostCode", | |
| "GetPostCodesWithTimeStamp", static_cast<uint16_t>(1))) | |
| .WillOnce([postcodes](const auto&, auto&& callback, const auto&, | |
| const auto&, const auto&, const auto&, uint16_t) { | |
| callback(boost::system::error_code(), postcodes); | |
| }); | |
| handleGetPostCodesEntryCollection(app_, CreateRequest(), share_async_resp_, | |
| "system1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["@odata.id"], | |
| "/redfish/v1/Systems/system1/LogServices/PostCodes/Entries"); | |
| EXPECT_EQ(json["Members@odata.count"], 1); | |
| ASSERT_EQ(json["Members"].size(), 1); | |
| EXPECT_EQ(json["Members"][0]["@odata.id"], | |
| "/redfish/v1/Systems/system1/LogServices/PostCodes/Entries/B1-1"); | |
| } | |
| TEST_F(SnapshotFixture, PostCodesEntryGetSuccess_SingleHost) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| boost::container::flat_map<uint64_t, | |
| std::tuple<uint64_t, std::vector<uint8_t>>> | |
| postcodes = { | |
| {1000000ULL, {0xb000a915ULL, {0x15, 0xa9, 0x00, 0xb0}}}, | |
| }; | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const boost::container::flat_map< | |
| uint64_t, managedStore::MockSerializedManagedObjectStore:: | |
| PostCodeEntry>&)>&&>(), | |
| "xyz.openbmc_project.State.Boot.PostCode0", | |
| "/xyz/openbmc_project/State/Boot/PostCode0", | |
| "xyz.openbmc_project.State.Boot.PostCode", | |
| "GetPostCodesWithTimeStamp", static_cast<uint16_t>(1))) | |
| .WillOnce([postcodes](const auto&, auto&& callback, const auto&, | |
| const auto&, const auto&, const auto&, uint16_t) { | |
| callback(boost::system::error_code(), postcodes); | |
| }); | |
| handleGetPostCodesEntry(app_, CreateRequest(), share_async_resp_, "system", | |
| "B1-1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["@odata.id"], | |
| "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/B1-1"); | |
| EXPECT_EQ(json["Id"], "B1-1"); | |
| EXPECT_EQ(json["MessageArgs"], | |
| std::vector<std::string>({"1", "0.0000", "0xb000a915"})); | |
| } | |
| TEST_F(SnapshotFixture_Platform23, PostCodesEntryGetSuccess_MultiHost) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockMultiSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/board/platform23/system1", | |
| "/xyz/openbmc_project/inventory/system/board/platform23/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockMultiSystemResponse)) | |
| .ok()); | |
| boost::container::flat_map<uint64_t, | |
| std::tuple<uint64_t, std::vector<uint8_t>>> | |
| postcodes = { | |
| {1000000ULL, {0xb000a915ULL, {0x15, 0xa9, 0x00, 0xb0}}}, | |
| }; | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const boost::container::flat_map< | |
| uint64_t, managedStore::MockSerializedManagedObjectStore:: | |
| PostCodeEntry>&)>&&>(), | |
| "xyz.openbmc_project.State.Boot.PostCode1", | |
| "/xyz/openbmc_project/State/Boot/PostCode1", | |
| "xyz.openbmc_project.State.Boot.PostCode", | |
| "GetPostCodesWithTimeStamp", static_cast<uint16_t>(1))) | |
| .WillOnce([postcodes](const auto&, auto&& callback, const auto&, | |
| const auto&, const auto&, const auto&, uint16_t) { | |
| callback(boost::system::error_code(), postcodes); | |
| }); | |
| handleGetPostCodesEntry(app_, CreateRequest(), share_async_resp_, "system1", | |
| "B1-1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| nlohmann::json& json = share_async_resp_->res.jsonValue; | |
| EXPECT_EQ(json["@odata.id"], | |
| "/redfish/v1/Systems/system1/LogServices/PostCodes/Entries/B1-1"); | |
| EXPECT_EQ(json["Id"], "B1-1"); | |
| } | |
| TEST_F(SnapshotFixture, PostCodesEntryAdditionalDataGetSuccess_SingleHost) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/system"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockSystemResponse)) | |
| .ok()); | |
| std::vector<std::tuple<uint64_t, std::vector<uint8_t>>> postcodes = { | |
| {1000000ULL, {'T', 'E', 'S', 'T'}}, | |
| }; | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const std::vector<managedStore::MockSerializedManagedObjectStore:: | |
| PostCodeEntry>&)>&&>(), | |
| "xyz.openbmc_project.State.Boot.PostCode0", | |
| "/xyz/openbmc_project/State/Boot/PostCode0", | |
| "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", | |
| static_cast<uint16_t>(1))) | |
| .WillOnce([postcodes](const auto&, auto&& callback, const auto&, | |
| const auto&, const auto&, const auto&, uint16_t) { | |
| callback(boost::system::error_code(), postcodes); | |
| }); | |
| handleGetPostCodesEntryAdditionalData(app_, CreateRequest(), | |
| share_async_resp_, "system", "B1-1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| EXPECT_EQ(share_async_resp_->res.getHeaderValue("Content-Type"), | |
| "application/octet-stream"); | |
| EXPECT_EQ(share_async_resp_->res.getHeaderValue("Content-Transfer-Encoding"), | |
| "Base64"); | |
| std::string decoded1; | |
| EXPECT_TRUE( | |
| crow::utility::base64Decode(share_async_resp_->res.body(), decoded1)); | |
| EXPECT_EQ(decoded1, "TEST"); | |
| } | |
| TEST_F(SnapshotFixture_Platform23, | |
| PostCodesEntryAdditionalDataGetSuccess_MultiHost) { | |
| KeyType systemKey(ManagedType::kManagedSubtreePaths, "/", 0, | |
| {kSystemInterfaces.begin(), kSystemInterfaces.end()}); | |
| MapperGetSubTreePathsResponse mockMultiSystemResponse = { | |
| "/xyz/openbmc_project/inventory/system/board/platform23/system1", | |
| "/xyz/openbmc_project/inventory/system/board/platform23/system2"}; | |
| ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()) | |
| ->upsertMockObjectIntoManagedStore( | |
| systemKey, CreateValueType(mockMultiSystemResponse)) | |
| .ok()); | |
| std::vector<std::tuple<uint64_t, std::vector<uint8_t>>> postcodes = { | |
| {1000000ULL, {'T', 'E', 'S', 'T'}}, | |
| }; | |
| EXPECT_CALL( | |
| *dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( | |
| managedStore::GetManagedObjectStore()), | |
| PostDbusCallToIoContextThreadSafe( | |
| _, | |
| An<absl::AnyInvocable<void( | |
| const boost::system::error_code&, | |
| const std::vector<managedStore::MockSerializedManagedObjectStore:: | |
| PostCodeEntry>&)>&&>(), | |
| "xyz.openbmc_project.State.Boot.PostCode1", | |
| "/xyz/openbmc_project/State/Boot/PostCode1", | |
| "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", | |
| static_cast<uint16_t>(1))) | |
| .WillOnce([postcodes](const auto&, auto&& callback, const auto&, | |
| const auto&, const auto&, const auto&, uint16_t) { | |
| callback(boost::system::error_code(), postcodes); | |
| }); | |
| handleGetPostCodesEntryAdditionalData(app_, CreateRequest(), | |
| share_async_resp_, "system1", "B1-1"); | |
| RunIoUntilDone(); | |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); | |
| EXPECT_EQ(share_async_resp_->res.getHeaderValue("Content-Type"), | |
| "application/octet-stream"); | |
| EXPECT_EQ(share_async_resp_->res.getHeaderValue("Content-Transfer-Encoding"), | |
| "Base64"); | |
| std::string decoded2; | |
| EXPECT_TRUE( | |
| crow::utility::base64Decode(share_async_resp_->res.body(), decoded2)); | |
| EXPECT_EQ(decoded2, "TEST"); | |
| } | |
| } // namespace | |
| } // namespace redfish |