blob: baa011f70a625e4c57e3223de5426400b8b1c788 [file] [log] [blame]
#include "app.hpp"
#include "async_resp.hpp"
#include "dbus_utility.hpp"
#include "event_service_manager.hpp"
#include "health.hpp"
#include "log_services.hpp"
#include "macros.hpp"
#include "snapshot_fixture.hpp"
#include "test/g3/mock_managed_store_test.hpp"
#include <nlohmann/json.hpp>
#include <map>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
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::ManagedObjectStoreContext;
using ::managedStore::ManagedType;
using ::managedStore::SimulateFailedAsyncPostDbusCallThreadSafeAction;
using ::managedStore::
SimulateFailedAsyncPostDbusCallThreadSafeWithEmptyValueAction;
using ::managedStore::SimulateFailedAsyncSetPropertyDbusCallAction;
using ::managedStore::SimulateSuccessfulAsyncPostDbusCallThreadSafeAction;
using ::managedStore::
SimulateSuccessfulAsyncPostDbusCallThreadSafeWithValueAction;
using ::managedStore::SimulateSuccessfulAsyncSetPropertyDbusCallAction;
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;
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(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(*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(*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(*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(*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(managedStore::GetManagedObjectStore()
->upsertMockObjectIntoManagedStore(key, mockIsRebooting)
.ok());
}
else
{
EXPECT_CALL(*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_TRUE(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
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(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(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/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(*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(*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(
*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(*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(*managedStore::GetManagedObjectStore(),
setProperty(pprFileObject, pprFilePath, pprFileInterface,
"RecordAdd",
dbus::utility::DbusVariantType{true}, _))
.Times(1)
.WillOnce(SimulateFailedAsyncSetPropertyDbusCallAction::
SimulateFailedAsyncSetPropertyDbusCall());
return;
}
EXPECT_CALL(*managedStore::GetManagedObjectStore(),
setProperty(pprFileObject, pprFilePath, pprFileInterface,
"RecordAdd", dbus::utility::DbusVariantType{true},
_))
.Times(1)
.WillOnce(SimulateSuccessfulAsyncSetPropertyDbusCallAction::
SimulateSuccessfulAsyncSetPropertyDbusCall());
if (dbusException >= 1)
{
EXPECT_CALL(
*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(*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, FailedOobPprEnableHandlePPRFilePatch)
{
// 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(managedStore::GetManagedObjectStore()
->upsertMockObjectIntoManagedStore(key, oobPprEnable)
.ok());
handlePPRFilePatch(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, SuccessfullyHandlePPRFilePatch)
{
KeyType key(ManagedType::kManagedProperty, pprFileObject,
sdbusplus::message::object_path(pprFilePath), pprFileInterface,
"oobPprEnable");
std::shared_ptr<ValueType> oobPprEnable =
CreateValueType(DbusVariantType(true));
EXPECT_TRUE(managedStore::GetManagedObjectStore()
->upsertMockObjectIntoManagedStore(key, oobPprEnable)
.ok());
prepareSetPostPackageRepairDataExpectCall(0);
handlePPRFilePatch(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(
*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(
*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(
*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"
"/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(*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, SuccessfullyhandlePPRConfigPatch)
{
EXPECT_CALL(*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)));
handlePPRConfigPatch(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",
"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.Crashdump"
),
std::make_pair(
"PrimaryLogId",
"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",
"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, QueryCrashDumpCollectionExpansion){
KeyType key1(ManagedType::kManagedObject, "xyz.openbmc_project.Dump.Manager",
sdbusplus::message::object_path("/xyz/openbmc_project/dump"));
ASSERT_TRUE(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(),
[this, &fileContent, &req, 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/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(),
[this, &fileContent, &req, 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> 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(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(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");
}
} // namespace
} // namespace redfish