blob: 7502dfa189de28b7d8aacfc424ba7fed035199b6 [file] [log] [blame]
/**
* Copyright © 2019 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "extensions/openpower-pels/paths.hpp"
#include "extensions/openpower-pels/repository.hpp"
#include "pel_utils.hpp"
#include <ext/stdio_filebuf.h>
#include <filesystem>
#include <gtest/gtest.h>
using namespace openpower::pels;
namespace fs = std::filesystem;
/**
* Clean the Repo after every testcase.
* And because we have PEL object, also clean up
* the log ID.
*/
class RepositoryTest : public CleanLogID
{
protected:
void SetUp() override
{
repoPath = getPELRepoPath();
}
void TearDown() override
{
fs::remove_all(repoPath);
}
fs::path repoPath;
};
TEST_F(RepositoryTest, FilenameTest)
{
BCDTime date = {0x20, 0x30, 0x11, 0x28, 0x13, 0x6, 0x7, 0x8};
EXPECT_EQ(Repository::getPELFilename(0x12345678, date),
"2030112813060708_12345678");
EXPECT_EQ(Repository::getPELFilename(0xAABBCCDD, date),
"2030112813060708_AABBCCDD");
EXPECT_EQ(Repository::getPELFilename(0x3AFF1, date),
"2030112813060708_0003AFF1");
EXPECT_EQ(Repository::getPELFilename(100, date),
"2030112813060708_00000064");
EXPECT_EQ(Repository::getPELFilename(0, date), "2030112813060708_00000000");
}
TEST_F(RepositoryTest, AddTest)
{
Repository repo{repoPath};
auto data = pelDataFactory(TestPELType::pelSimple);
auto pel = std::make_unique<PEL>(data);
repo.add(pel);
// Check that the PEL was stored where it was supposed to be,
// and that it wrote the PEL data.
const auto ts = pel->privateHeader().commitTimestamp();
auto name = Repository::getPELFilename(pel->id(), ts);
fs::path file = repoPath / "logs" / name;
EXPECT_TRUE(fs::exists(file));
auto newData = readPELFile(file);
auto pelData = pel->data();
EXPECT_EQ(*newData, pelData);
EXPECT_EQ(repo.lastPelID(), pel->id());
}
TEST_F(RepositoryTest, RemoveTest)
{
using pelID = Repository::LogID::Pel;
using obmcID = Repository::LogID::Obmc;
// Add and remove a PEL from the repo
Repository repo{repoPath};
auto data = pelDataFactory(TestPELType::pelSimple);
auto pel = std::make_unique<PEL>(data, 1);
pel->assignID();
Repository::LogID id{pelID{pel->id()}, obmcID{pel->obmcLogID()}};
repo.add(pel);
auto removedID = repo.remove(id);
ASSERT_TRUE(removedID);
EXPECT_EQ(*removedID, id);
EXPECT_FALSE(repo.hasPEL(id));
// Try to remove it again, not there
EXPECT_FALSE(repo.remove(id));
}
TEST_F(RepositoryTest, RestoreTest)
{
using pelID = Repository::LogID::Pel;
using obmcID = Repository::LogID::Obmc;
std::vector<Repository::LogID> ids;
{
Repository repo{repoPath};
// Add some PELs to the repository
{
auto data = pelDataFactory(TestPELType::pelSimple);
auto pel = std::make_unique<PEL>(data, 1);
pel->assignID();
repo.add(pel);
ids.emplace_back(pelID(pel->id()), obmcID(1));
}
{
auto data = pelDataFactory(TestPELType::pelSimple);
auto pel = std::make_unique<PEL>(data, 2);
pel->assignID();
repo.add(pel);
ids.emplace_back(pelID(pel->id()), obmcID(2));
}
// Check they're there
EXPECT_TRUE(repo.hasPEL(ids[0]));
EXPECT_TRUE(repo.hasPEL(ids[1]));
// Do some other search tests while we're here.
// Search based on PEL ID
Repository::LogID id(pelID(ids[0].pelID));
EXPECT_TRUE(repo.hasPEL(id));
// Search based on OBMC log ID
id.pelID.id = 0;
id.obmcID = ids[0].obmcID;
EXPECT_TRUE(repo.hasPEL(id));
// ... based on the other PEL ID
id.pelID = ids[1].pelID;
id.obmcID.id = 0;
EXPECT_TRUE(repo.hasPEL(id));
// Not found
id.pelID.id = 99;
id.obmcID.id = 100;
EXPECT_FALSE(repo.hasPEL(id));
// Try to remove it anyway
EXPECT_FALSE(repo.remove(id));
}
{
// Restore and check they're still there, then
// remove them.
Repository repo{repoPath};
EXPECT_TRUE(repo.hasPEL(ids[0]));
EXPECT_TRUE(repo.hasPEL(ids[1]));
repo.remove(ids[0]);
EXPECT_FALSE(repo.hasPEL(ids[0]));
repo.remove(ids[1]);
EXPECT_FALSE(repo.hasPEL(ids[1]));
}
}
TEST_F(RepositoryTest, TestGetPELData)
{
using ID = Repository::LogID;
Repository repo{repoPath};
ID badID{ID::Pel(42)};
auto noData = repo.getPELData(badID);
EXPECT_FALSE(noData);
// Add a PEL to the repo, and get the data back with getPELData.
auto data = pelDataFactory(TestPELType::pelSimple);
auto dataCopy = data;
auto pel = std::make_unique<PEL>(data);
auto pelID = pel->id();
repo.add(pel);
ID id{ID::Pel(pelID)};
auto pelData = repo.getPELData(id);
ASSERT_TRUE(pelData);
EXPECT_EQ(dataCopy, *pelData);
}
TEST_F(RepositoryTest, TestForEach)
{
Repository repo{repoPath};
// Add 2 PELs
auto data = pelDataFactory(TestPELType::pelSimple);
auto pel = std::make_unique<PEL>(data);
repo.add(pel);
pel = std::make_unique<PEL>(data);
pel->assignID();
pel->setCommitTime();
repo.add(pel);
// Make a function that saves the IDs
std::vector<uint32_t> ids;
Repository::ForEachFunc f1 = [&ids](const PEL& pel) {
ids.push_back(pel.id());
return false;
};
repo.for_each(f1);
EXPECT_EQ(ids.size(), 2);
// Stop after the first time in.
Repository::ForEachFunc f2 = [&ids](const PEL& pel) {
ids.push_back(pel.id());
return true;
};
ids.clear();
repo.for_each(f2);
EXPECT_EQ(ids.size(), 1);
}
TEST_F(RepositoryTest, TestSubscriptions)
{
std::vector<uint32_t> added;
std::vector<uint32_t> removed;
Repository::AddCallback ac = [&added](const PEL& pel) {
added.push_back(pel.id());
};
Repository::DeleteCallback dc = [&removed](uint32_t id) {
removed.push_back(id);
};
Repository repo{repoPath};
repo.subscribeToAdds("test", ac);
repo.subscribeToDeletes("test", dc);
auto data = pelDataFactory(TestPELType::pelSimple);
auto pel = std::make_unique<PEL>(data);
auto pelID = pel->id();
repo.add(pel);
EXPECT_EQ(added.size(), 1);
using ID = Repository::LogID;
ID id{ID::Pel(pelID)};
repo.remove(id);
EXPECT_EQ(removed.size(), 1);
repo.unsubscribeFromAdds("test");
repo.unsubscribeFromDeletes("test");
added.clear();
removed.clear();
repo.add(pel);
EXPECT_EQ(added.size(), 0);
repo.remove(id);
EXPECT_EQ(removed.size(), 0);
}
TEST_F(RepositoryTest, TestGetAttributes)
{
uint32_t pelID = 0;
std::bitset<16> actionFlags;
{
Repository repo{repoPath};
// Add a PEL to the repo
auto data = pelDataFactory(TestPELType::pelSimple);
auto pel = std::make_unique<PEL>(data);
repo.add(pel);
pelID = pel->id();
actionFlags = pel->userHeader().actionFlags();
using ID = Repository::LogID;
ID id{ID::Pel(pelID)};
auto a = repo.getPELAttributes(id);
EXPECT_TRUE(a);
EXPECT_EQ((*a).get().actionFlags, actionFlags);
id.pelID.id = 0;
a = repo.getPELAttributes(id);
EXPECT_FALSE(a);
}
{
// Restore the repository and check again
Repository repo{repoPath};
using ID = Repository::LogID;
ID id{ID::Pel(pelID)};
auto a = repo.getPELAttributes(id);
EXPECT_TRUE(a);
EXPECT_EQ((*a).get().actionFlags, actionFlags);
id.pelID.id = 0;
a = repo.getPELAttributes(id);
EXPECT_FALSE(a);
}
}
TEST_F(RepositoryTest, TestSetHostState)
{
// Add a PEL to the repo
auto data = pelDataFactory(TestPELType::pelSimple);
auto pel = std::make_unique<PEL>(data);
using ID = Repository::LogID;
ID id{ID::Pel(pel->id())};
{
Repository repo{repoPath};
repo.add(pel);
auto a = repo.getPELAttributes(id);
EXPECT_EQ((*a).get().hostState, TransmissionState::newPEL);
repo.setPELHostTransState(pel->id(), TransmissionState::acked);
// First, check the attributes
a = repo.getPELAttributes(id);
EXPECT_EQ((*a).get().hostState, TransmissionState::acked);
// Next, check the PEL data itself
auto pelData = repo.getPELData(id);
PEL newPEL{*pelData};
EXPECT_EQ(newPEL.hostTransmissionState(), TransmissionState::acked);
}
{
// Now restore, and check again
Repository repo{repoPath};
// First, check the attributes
auto a = repo.getPELAttributes(id);
EXPECT_EQ((*a).get().hostState, TransmissionState::acked);
// Next, check the PEL data itself
auto pelData = repo.getPELData(id);
PEL newPEL{*pelData};
EXPECT_EQ(newPEL.hostTransmissionState(), TransmissionState::acked);
}
}
TEST_F(RepositoryTest, TestSetHMCState)
{
// Add a PEL to the repo
auto data = pelDataFactory(TestPELType::pelSimple);
auto pel = std::make_unique<PEL>(data);
using ID = Repository::LogID;
ID id{ID::Pel(pel->id())};
{
Repository repo{repoPath};
repo.add(pel);
auto a = repo.getPELAttributes(id);
EXPECT_EQ((*a).get().hmcState, TransmissionState::newPEL);
repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
// First, check the attributes
a = repo.getPELAttributes(id);
EXPECT_EQ((*a).get().hmcState, TransmissionState::acked);
// Next, check the PEL data itself
auto pelData = repo.getPELData(id);
PEL newPEL{*pelData};
EXPECT_EQ(newPEL.hmcTransmissionState(), TransmissionState::acked);
}
{
// Now restore, and check again
Repository repo{repoPath};
// First, check the attributes
auto a = repo.getPELAttributes(id);
EXPECT_EQ((*a).get().hmcState, TransmissionState::acked);
// Next, check the PEL data itself
auto pelData = repo.getPELData(id);
PEL newPEL{*pelData};
EXPECT_EQ(newPEL.hmcTransmissionState(), TransmissionState::acked);
}
}
TEST_F(RepositoryTest, TestGetPELFD)
{
Repository repo{repoPath};
auto data = pelDataFactory(TestPELType::pelSimple);
auto pel = std::make_unique<PEL>(data);
pel->setCommitTime();
pel->assignID();
repo.add(pel);
using ID = Repository::LogID;
ID id{ID::Pel(pel->id())};
auto fd = repo.getPELFD(id);
EXPECT_TRUE(fd);
// Get the size
struct stat s;
int r = fstat(*fd, &s);
ASSERT_EQ(r, 0);
auto size = s.st_size;
// Read the PEL data out of the FD
FILE* fp = fdopen(*fd, "r");
ASSERT_NE(fp, nullptr);
std::vector<uint8_t> newData;
newData.resize(size);
r = fread(newData.data(), 1, size, fp);
EXPECT_EQ(r, size);
PEL newPEL{newData};
EXPECT_TRUE(newPEL.valid());
EXPECT_EQ(newPEL.id(), pel->id());
fclose(fp);
// Call getPELFD again, this time with a bad ID
id.pelID.id = 42;
fd = repo.getPELFD(id);
EXPECT_FALSE(fd);
}
// Test the repo size statistics
TEST_F(RepositoryTest, TestRepoSizes)
{
uint32_t id = 1;
Repository repo{repoPath, 10000, 500};
// All of the size stats are the sizes on disk a PEL takes up,
// which is different than the file size. Disk usage seems
// to have a granularity of 4096 bytes. This probably shouldn't
// be hardcoded, but I don't know how to look it up dynamically.
// All sizes are zero
{
const auto& stats = repo.getSizeStats();
EXPECT_EQ(stats.total, 0);
EXPECT_EQ(stats.bmc, 0);
EXPECT_EQ(stats.nonBMC, 0);
EXPECT_EQ(stats.bmcServiceable, 0);
EXPECT_EQ(stats.bmcInfo, 0);
EXPECT_EQ(stats.nonBMCServiceable, 0);
EXPECT_EQ(stats.nonBMCInfo, 0);
}
// Add a 2000B BMC predictive error
auto data = pelFactory(id++, 'O', 0x20, 0x8800, 2000);
auto pel = std::make_unique<PEL>(data);
auto pelID1 = pel->id();
repo.add(pel);
{
const auto& stats = repo.getSizeStats();
EXPECT_EQ(stats.total, 4096);
EXPECT_EQ(stats.bmc, 4096);
EXPECT_EQ(stats.nonBMC, 0);
EXPECT_EQ(stats.bmcServiceable, 4096);
EXPECT_EQ(stats.bmcInfo, 0);
EXPECT_EQ(stats.nonBMCServiceable, 0);
EXPECT_EQ(stats.nonBMCInfo, 0);
}
// Add a 5000B BMC informational error
data = pelFactory(id++, 'O', 0x00, 0x8800, 5000);
pel = std::make_unique<PEL>(data);
auto pelID2 = pel->id();
repo.add(pel);
{
const auto& stats = repo.getSizeStats();
EXPECT_EQ(stats.total, 4096 + 8192);
EXPECT_EQ(stats.bmc, 4096 + 8192);
EXPECT_EQ(stats.nonBMC, 0);
EXPECT_EQ(stats.bmcServiceable, 4096);
EXPECT_EQ(stats.bmcInfo, 8192);
EXPECT_EQ(stats.nonBMCServiceable, 0);
EXPECT_EQ(stats.nonBMCInfo, 0);
}
// Add a 4000B Hostboot unrecoverable error
data = pelFactory(id++, 'B', 0x40, 0x8800, 4000);
pel = std::make_unique<PEL>(data);
auto pelID3 = pel->id();
repo.add(pel);
{
const auto& stats = repo.getSizeStats();
EXPECT_EQ(stats.total, 4096 + 8192 + 4096);
EXPECT_EQ(stats.bmc, 4096 + 8192);
EXPECT_EQ(stats.nonBMC, 4096);
EXPECT_EQ(stats.bmcServiceable, 4096);
EXPECT_EQ(stats.bmcInfo, 8192);
EXPECT_EQ(stats.nonBMCServiceable, 4096);
EXPECT_EQ(stats.nonBMCInfo, 0);
}
// Add a 5000B Hostboot informational error
data = pelFactory(id++, 'B', 0x00, 0x8800, 5000);
pel = std::make_unique<PEL>(data);
auto pelID4 = pel->id();
repo.add(pel);
{
const auto& stats = repo.getSizeStats();
EXPECT_EQ(stats.total, 4096 + 8192 + 4096 + 8192);
EXPECT_EQ(stats.bmc, 4096 + 8192);
EXPECT_EQ(stats.nonBMC, 4096 + 8192);
EXPECT_EQ(stats.bmcServiceable, 4096);
EXPECT_EQ(stats.bmcInfo, 8192);
EXPECT_EQ(stats.nonBMCServiceable, 4096);
EXPECT_EQ(stats.nonBMCInfo, 8192);
}
// Remove the BMC serviceable error
using ID = Repository::LogID;
ID id1{ID::Pel(pelID1)};
repo.remove(id1);
{
const auto& stats = repo.getSizeStats();
EXPECT_EQ(stats.total, 8192 + 4096 + 8192);
EXPECT_EQ(stats.bmc, 8192);
EXPECT_EQ(stats.nonBMC, 4096 + 8192);
EXPECT_EQ(stats.bmcServiceable, 0);
EXPECT_EQ(stats.bmcInfo, 8192);
EXPECT_EQ(stats.nonBMCServiceable, 4096);
EXPECT_EQ(stats.nonBMCInfo, 8192);
}
// Remove the Hostboot informational error
ID id4{ID::Pel(pelID4)};
repo.remove(id4);
{
const auto& stats = repo.getSizeStats();
EXPECT_EQ(stats.total, 8192 + 4096);
EXPECT_EQ(stats.bmc, 8192);
EXPECT_EQ(stats.nonBMC, 4096);
EXPECT_EQ(stats.bmcServiceable, 0);
EXPECT_EQ(stats.bmcInfo, 8192);
EXPECT_EQ(stats.nonBMCServiceable, 4096);
EXPECT_EQ(stats.nonBMCInfo, 0);
}
// Remove the BMC informational error
ID id2{ID::Pel(pelID2)};
repo.remove(id2);
{
const auto& stats = repo.getSizeStats();
EXPECT_EQ(stats.total, 4096);
EXPECT_EQ(stats.bmc, 0);
EXPECT_EQ(stats.nonBMC, 4096);
EXPECT_EQ(stats.bmcServiceable, 0);
EXPECT_EQ(stats.bmcInfo, 0);
EXPECT_EQ(stats.nonBMCServiceable, 4096);
EXPECT_EQ(stats.nonBMCInfo, 0);
}
// Remove the hostboot unrecoverable error
ID id3{ID::Pel(pelID3)};
repo.remove(id3);
{
const auto& stats = repo.getSizeStats();
EXPECT_EQ(stats.total, 0);
EXPECT_EQ(stats.bmc, 0);
EXPECT_EQ(stats.nonBMC, 0);
EXPECT_EQ(stats.bmcServiceable, 0);
EXPECT_EQ(stats.bmcInfo, 0);
EXPECT_EQ(stats.nonBMCServiceable, 0);
EXPECT_EQ(stats.nonBMCInfo, 0);
}
}
// Prune PELs, when no HMC/OS/PHYP acks
TEST_F(RepositoryTest, TestPruneNoAcks)
{
std::vector<uint32_t> id;
Repository repo{repoPath, 4096 * 20, 100};
// Add 10 4096B (on disk) PELs of BMC nonInfo, Info and nonBMC info,
// nonInfo errors. None of them acked by PHYP, host, or HMC.
for (uint32_t i = 1; i <= 10; i++)
{
// BMC predictive
auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
auto pel = std::make_unique<PEL>(data);
repo.add(pel);
// BMC info
data = pelFactory(i + 100, 'O', 0x0, 0x8800, 500);
pel = std::make_unique<PEL>(data);
repo.add(pel);
// Hostboot predictive
data = pelFactory(i + 200, 'B', 0x20, 0x8800, 500);
pel = std::make_unique<PEL>(data);
repo.add(pel);
// Hostboot info
data = pelFactory(i + 300, 'B', 0x0, 0x8800, 500);
pel = std::make_unique<PEL>(data);
repo.add(pel);
}
const auto& sizes = repo.getSizeStats();
EXPECT_EQ(sizes.total, 4096 * 40);
// Sanity check the very first PELs with IDs 1 to 4 are
// there so we can check they are removed after the prune.
for (uint32_t i = 1; i < 5; i++)
{
Repository::LogID logID{Repository::LogID::Pel{i}};
EXPECT_TRUE(repo.getPELAttributes(logID));
}
// Prune down to 15%/30%/15%/30% = 90% total
auto IDs = repo.prune(id);
// Check the final sizes
EXPECT_EQ(sizes.total, 4096 * 18); // 90% of 20 PELs
EXPECT_EQ(sizes.bmcInfo, 4096 * 3); // 15% of 20 PELs
EXPECT_EQ(sizes.bmcServiceable, 4096 * 6); // 30% of 20 PELs
EXPECT_EQ(sizes.nonBMCInfo, 4096 * 3); // 15% of 20 PELs
EXPECT_EQ(sizes.nonBMCServiceable, 4096 * 6); // 30% of 20 PELs
// Check that at least the 4 oldest, which are the oldest of
// each type, were removed.
for (uint32_t i = 1; i < 5; i++)
{
Repository::LogID logID{Repository::LogID::Pel{i}};
EXPECT_FALSE(repo.getPELAttributes(logID));
// Make sure the corresponding OpenBMC event log ID which is
// 500 + the PEL ID is in the list.
EXPECT_TRUE(std::find(IDs.begin(), IDs.end(), 500 + i) != IDs.end());
}
}
// Test that if filled completely with 1 type of PEL, that
// pruning still works properly
TEST_F(RepositoryTest, TestPruneInfoOnly)
{
std::vector<uint32_t> id;
Repository repo{repoPath, 4096 * 22, 100};
// Fill 4096*23 bytes on disk of BMC info PELs
for (uint32_t i = 1; i <= 23; i++)
{
auto data = pelFactory(i, 'O', 0, 0x8800, 1000);
auto pel = std::make_unique<PEL>(data);
repo.add(pel);
}
const auto& sizes = repo.getSizeStats();
EXPECT_EQ(sizes.total, 4096 * 23);
// Pruning to 15% of 4096 * 22 will leave 3 4096B PELs.
// Sanity check the oldest 20 are there so when they
// get pruned below we'll know they were removed.
for (uint32_t i = 1; i <= 20; i++)
{
Repository::LogID logID{Repository::LogID::Pel{i}};
EXPECT_TRUE(repo.getPELAttributes(logID));
}
auto IDs = repo.prune(id);
// Check the final sizes
EXPECT_EQ(sizes.total, 4096 * 3);
EXPECT_EQ(sizes.bmcInfo, 4096 * 3);
EXPECT_EQ(sizes.bmcServiceable, 0);
EXPECT_EQ(sizes.nonBMCInfo, 0);
EXPECT_EQ(sizes.nonBMCServiceable, 0);
EXPECT_EQ(IDs.size(), 20);
// Can no longer find the oldest 20 PELs.
for (uint32_t i = 1; i <= 20; i++)
{
Repository::LogID logID{Repository::LogID::Pel{i}};
EXPECT_FALSE(repo.getPELAttributes(logID));
EXPECT_TRUE(std::find(IDs.begin(), IDs.end(), 500 + i) != IDs.end());
}
}
// Test that the HMC/OS/PHYP ack values affect the
// pruning order.
TEST_F(RepositoryTest, TestPruneWithAcks)
{
std::vector<uint32_t> id;
Repository repo{repoPath, 4096 * 20, 100};
// Fill 30% worth of BMC non-info non-acked PELs
for (uint32_t i = 1; i <= 6; i++)
{
// BMC predictive
auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
auto pel = std::make_unique<PEL>(data);
repo.add(pel);
}
// Add another PEL to push it over the 30%, each time adding
// a different type that should be pruned before the above ones
// even though those are older.
for (uint32_t i = 1; i <= 3; i++)
{
auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
auto pel = std::make_unique<PEL>(data);
auto idToDelete = pel->obmcLogID();
repo.add(pel);
if (1 == i)
{
repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
}
else if (2 == i)
{
repo.setPELHostTransState(pel->id(), TransmissionState::acked);
}
else
{
repo.setPELHostTransState(pel->id(), TransmissionState::sent);
}
auto IDs = repo.prune(id);
EXPECT_EQ(repo.getSizeStats().total, 4096 * 6);
// The newest PEL should be the one deleted
ASSERT_EQ(IDs.size(), 1);
EXPECT_EQ(IDs[0], idToDelete);
}
}
// Test that the total number of PELs limit is enforced.
TEST_F(RepositoryTest, TestPruneTooManyPELs)
{
std::vector<uint32_t> id;
Repository repo{repoPath, 4096 * 100, 10};
// Add 10, which is the limit and is still OK
for (uint32_t i = 1; i <= 10; i++)
{
auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
auto pel = std::make_unique<PEL>(data);
repo.add(pel);
}
auto IDs = repo.prune(id);
// Nothing pruned yet
EXPECT_TRUE(IDs.empty());
// Add 1 more PEL which will be too many.
{
auto data = pelFactory(11, 'O', 0x20, 0x8800, 500);
auto pel = std::make_unique<PEL>(data);
repo.add(pel);
}
// Now that's it's over the limit of 10, it will bring it down
// to 80%, which is 8 after it removes 3.
IDs = repo.prune(id);
EXPECT_EQ(repo.getSizeStats().total, 4096 * 8);
ASSERT_EQ(IDs.size(), 3);
// Check that it deleted the oldest ones.
// The OpenBMC log ID is the PEL ID + 500.
EXPECT_EQ(IDs[0], 500 + 1);
EXPECT_EQ(IDs[1], 500 + 2);
EXPECT_EQ(IDs[2], 500 + 3);
}
// Test the sizeWarning function
TEST_F(RepositoryTest, TestSizeWarning)
{
uint32_t id = 1;
Repository repo{repoPath, 100 * 4096, 500};
EXPECT_FALSE(repo.sizeWarning());
// 95% is still OK (disk size for these is 4096)
for (uint32_t i = 1; i <= 95; i++)
{
auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
auto pel = std::make_unique<PEL>(data);
repo.add(pel);
}
EXPECT_FALSE(repo.sizeWarning());
// Now at 96%
auto data = pelFactory(id++, 'B', 0x20, 0x8800, 500);
auto pel = std::make_unique<PEL>(data);
repo.add(pel);
EXPECT_TRUE(repo.sizeWarning());
}
// Test sizeWarning when there are too many PEls
TEST_F(RepositoryTest, TestSizeWarningNumPELs)
{
Repository repo{repoPath, 4096 * 100, 5};
EXPECT_FALSE(repo.sizeWarning());
for (uint32_t i = 1; i <= 5; i++)
{
auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
auto pel = std::make_unique<PEL>(data);
repo.add(pel);
}
EXPECT_FALSE(repo.sizeWarning());
// Add 1 more for a total of 6, now over the limit
{
auto data = pelFactory(6, 'O', 0x20, 0x8800, 500);
auto pel = std::make_unique<PEL>(data);
repo.add(pel);
}
EXPECT_TRUE(repo.sizeWarning());
}
// Test existense of archive file
TEST_F(RepositoryTest, TestArchiveFile)
{
using pelID = Repository::LogID::Pel;
using obmcID = Repository::LogID::Obmc;
// Add and remove a PEL from the repo
Repository repo{repoPath};
fs::path archivePath = repoPath / "logs" / "archive";
EXPECT_TRUE(fs::exists(archivePath));
auto data = pelDataFactory(TestPELType::pelSimple);
auto pel = std::make_unique<PEL>(data, 1);
pel->assignID();
Repository::LogID id{pelID{pel->id()}, obmcID{pel->obmcLogID()}};
repo.add(pel);
auto path = repoPath / "logs" /
Repository::getPELFilename(pel->id(), pel->commitTime());
EXPECT_TRUE(fs::exists(path));
auto removedID = repo.remove(id);
ASSERT_TRUE(removedID);
EXPECT_EQ(*removedID, id);
archivePath /= Repository::getPELFilename(pel->id(), pel->commitTime());
EXPECT_TRUE(fs::exists(archivePath));
EXPECT_FALSE(repo.hasPEL(id));
}
// Test archive folder size with sizeWarning function
TEST_F(RepositoryTest, TestArchiveSize)
{
using pelID = Repository::LogID::Pel;
using obmcID = Repository::LogID::Obmc;
// Create repo with max PEL=500 and space=4096*100
Repository repo{repoPath, 100 * 4096, 500};
// Fill 94% (disk size for these is 4096)
for (uint32_t i = 1; i <= 94; i++)
{
auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
auto pel = std::make_unique<PEL>(data);
repo.add(pel);
}
// Add another PEL which makes 95% still ok
auto data = pelDataFactory(TestPELType::pelSimple);
auto pel = std::make_unique<PEL>(data, 1);
pel->assignID();
Repository::LogID id{pelID{pel->id()}, obmcID{pel->obmcLogID()}};
repo.add(pel);
// With 95% full expect no size warning
EXPECT_FALSE(repo.sizeWarning());
// Remove last created PEL
repo.remove(id);
// Repo is 94% full with one PEL in archive log
// Total repo size 95% full (including archive) still ok
EXPECT_FALSE(repo.sizeWarning());
// Confirm the repo size 94% full
const auto& sizes = repo.getSizeStats();
EXPECT_EQ(sizes.total, 4096 * 94);
// Make sure archive contain the one deleted file
fs::path archivePath = repoPath / "logs" / "archive";
archivePath /= Repository::getPELFilename(pel->id(), pel->commitTime());
EXPECT_TRUE(fs::exists(archivePath));
// Add another PEL which makes repo 95% full
data = pelDataFactory(TestPELType::pelSimple);
pel = std::make_unique<PEL>(data, 1);
pel->assignID();
Repository::LogID idx{pelID{pel->id()}, obmcID{pel->obmcLogID()}};
repo.add(pel);
// Repo with 95% full + one archive file becomes 96%
// which is greater than the warning
// expect archive file to be deleted to get repo size back to 95%
EXPECT_FALSE(repo.sizeWarning());
EXPECT_FALSE(fs::exists(archivePath));
}
TEST_F(RepositoryTest, GetLogIDFoundTC)
{
// Add and Check the created LogId
Repository repo{repoPath};
auto data = pelDataFactory(TestPELType::pelSimple);
auto pel = std::make_unique<PEL>(data, 1);
pel->assignID();
repo.add(pel);
// Getting by PEL Id
Repository::LogID idWithPelId{Repository::LogID::Pel(pel->id())};
auto logID = repo.getLogID(idWithPelId);
ASSERT_TRUE(logID.has_value());
EXPECT_EQ(logID->obmcID.id, pel->obmcLogID());
EXPECT_EQ(logID->pelID.id, pel->id());
// Getting by OBMC Event Log Id
Repository::LogID idWithObmcLogId{
Repository::LogID::Obmc(pel->obmcLogID())};
logID = repo.getLogID(idWithObmcLogId);
ASSERT_TRUE(logID.has_value());
EXPECT_EQ(logID->obmcID.id, pel->obmcLogID());
EXPECT_EQ(logID->pelID.id, pel->id());
}
TEST_F(RepositoryTest, GetLogIDNotFoundTC)
{
// Add and Check the created LogId
Repository repo{repoPath};
auto data = pelDataFactory(TestPELType::pelSimple);
auto pel = std::make_unique<PEL>(data, 1);
pel->assignID();
repo.add(pel);
// Getting by invalid PEL Id
Repository::LogID idWithPelId{Repository::LogID::Pel(0xFFFFFFFF)};
auto logID = repo.getLogID(idWithPelId);
ASSERT_TRUE(!logID.has_value());
// Getting by invalid OBMC Event Log ID
Repository::LogID idWithObmcLogId{Repository::LogID::Obmc(0xFFFFFFFF)};
logID = repo.getLogID(idWithObmcLogId);
ASSERT_TRUE(!logID.has_value());
}
// Test that OpenBMC log Id with hardware isolation entry is not removed.
TEST_F(RepositoryTest, TestPruneWithIdHwIsoEntry)
{
std::vector<uint32_t> id{502};
Repository repo{repoPath, 4096 * 100, 10};
// Add 10, which is the limit and is still OK
for (uint32_t i = 1; i <= 10; i++)
{
auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
auto pel = std::make_unique<PEL>(data);
repo.add(pel);
}
auto IDs = repo.prune(id);
// Nothing pruned yet
EXPECT_TRUE(IDs.empty());
// Add 1 more PEL which will be too many.
{
auto data = pelFactory(11, 'O', 0x20, 0x8800, 500);
auto pel = std::make_unique<PEL>(data);
repo.add(pel);
}
// Now that's it's over the limit of 10, it will bring it down
// to 80%, which is 8 after it removes 3.
IDs = repo.prune(id);
EXPECT_EQ(repo.getSizeStats().total, 4096 * 8);
ASSERT_EQ(IDs.size(), 3);
// Check that it deleted the oldest ones.
// And the Id with hw isolation entry is NOT removed.
// The OpenBMC log ID is the PEL ID + 500.
EXPECT_EQ(IDs[0], 500 + 1);
EXPECT_EQ(IDs[1], 500 + 3);
EXPECT_EQ(IDs[2], 500 + 4);
}