| /** |
| * 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); |
| } |