blob: 79b9cc6a8071f28b1dd0e4a849887eecb8627e08 [file]
#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 = "") {
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));
}
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);
ofs << "CrashdumpPayload123";
}
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);
}
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);
ofs << "test_crashdump_content";
}
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);
ofs << "CRASHDUMP_TEST_PAYLOAD";
}
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);
ofs << "custom_log_property_content";
}
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