1 /**
2  * Copyright © 2019 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "extensions/openpower-pels/paths.hpp"
17 #include "extensions/openpower-pels/repository.hpp"
18 #include "pel_utils.hpp"
19 
20 #include <ext/stdio_filebuf.h>
21 
22 #include <filesystem>
23 
24 #include <gtest/gtest.h>
25 
26 using namespace openpower::pels;
27 namespace fs = std::filesystem;
28 
29 /**
30  * Clean the Repo after every testcase.
31  * And because we have PEL object, also clean up
32  * the log ID.
33  */
34 class RepositoryTest : public CleanLogID
35 {
36   protected:
SetUp()37     void SetUp() override
38     {
39         repoPath = getPELRepoPath();
40     }
41 
TearDown()42     void TearDown() override
43     {
44         fs::remove_all(repoPath);
45     }
46 
47     fs::path repoPath;
48 };
49 
TEST_F(RepositoryTest,FilenameTest)50 TEST_F(RepositoryTest, FilenameTest)
51 {
52     BCDTime date = {0x20, 0x30, 0x11, 0x28, 0x13, 0x6, 0x7, 0x8};
53 
54     EXPECT_EQ(Repository::getPELFilename(0x12345678, date),
55               "2030112813060708_12345678");
56 
57     EXPECT_EQ(Repository::getPELFilename(0xAABBCCDD, date),
58               "2030112813060708_AABBCCDD");
59 
60     EXPECT_EQ(Repository::getPELFilename(0x3AFF1, date),
61               "2030112813060708_0003AFF1");
62 
63     EXPECT_EQ(Repository::getPELFilename(100, date),
64               "2030112813060708_00000064");
65 
66     EXPECT_EQ(Repository::getPELFilename(0, date), "2030112813060708_00000000");
67 }
68 
TEST_F(RepositoryTest,AddTest)69 TEST_F(RepositoryTest, AddTest)
70 {
71     Repository repo{repoPath};
72     auto data = pelDataFactory(TestPELType::pelSimple);
73     auto pel = std::make_unique<PEL>(data);
74 
75     repo.add(pel);
76 
77     // Check that the PEL was stored where it was supposed to be,
78     // and that it wrote the PEL data.
79     const auto ts = pel->privateHeader().commitTimestamp();
80     auto name = Repository::getPELFilename(pel->id(), ts);
81 
82     fs::path file = repoPath / "logs" / name;
83     EXPECT_TRUE(fs::exists(file));
84 
85     auto newData = readPELFile(file);
86     auto pelData = pel->data();
87     EXPECT_EQ(*newData, pelData);
88 
89     EXPECT_EQ(repo.lastPelID(), pel->id());
90 }
91 
TEST_F(RepositoryTest,RemoveTest)92 TEST_F(RepositoryTest, RemoveTest)
93 {
94     using pelID = Repository::LogID::Pel;
95     using obmcID = Repository::LogID::Obmc;
96 
97     // Add and remove a PEL from the repo
98 
99     Repository repo{repoPath};
100 
101     auto data = pelDataFactory(TestPELType::pelSimple);
102     auto pel = std::make_unique<PEL>(data, 1);
103 
104     pel->assignID();
105     Repository::LogID id{pelID{pel->id()}, obmcID{pel->obmcLogID()}};
106 
107     repo.add(pel);
108 
109     auto removedID = repo.remove(id);
110     ASSERT_TRUE(removedID);
111     EXPECT_EQ(*removedID, id);
112 
113     EXPECT_FALSE(repo.hasPEL(id));
114 
115     // Try to remove it again, not there
116     EXPECT_FALSE(repo.remove(id));
117 }
118 
TEST_F(RepositoryTest,RestoreTest)119 TEST_F(RepositoryTest, RestoreTest)
120 {
121     using pelID = Repository::LogID::Pel;
122     using obmcID = Repository::LogID::Obmc;
123 
124     std::vector<Repository::LogID> ids;
125 
126     {
127         Repository repo{repoPath};
128 
129         // Add some PELs to the repository
130         {
131             auto data = pelDataFactory(TestPELType::pelSimple);
132             auto pel = std::make_unique<PEL>(data, 1);
133             pel->assignID();
134             repo.add(pel);
135             ids.emplace_back(pelID(pel->id()), obmcID(1));
136         }
137         {
138             auto data = pelDataFactory(TestPELType::pelSimple);
139             auto pel = std::make_unique<PEL>(data, 2);
140             pel->assignID();
141             repo.add(pel);
142             ids.emplace_back(pelID(pel->id()), obmcID(2));
143         }
144 
145         // Check they're there
146         EXPECT_TRUE(repo.hasPEL(ids[0]));
147         EXPECT_TRUE(repo.hasPEL(ids[1]));
148 
149         // Do some other search tests while we're here.
150 
151         // Search based on PEL ID
152         Repository::LogID id(pelID(ids[0].pelID));
153         EXPECT_TRUE(repo.hasPEL(id));
154 
155         // Search based on OBMC log ID
156         id.pelID.id = 0;
157         id.obmcID = ids[0].obmcID;
158         EXPECT_TRUE(repo.hasPEL(id));
159 
160         // ... based on the other PEL ID
161         id.pelID = ids[1].pelID;
162         id.obmcID.id = 0;
163         EXPECT_TRUE(repo.hasPEL(id));
164 
165         // Not found
166         id.pelID.id = 99;
167         id.obmcID.id = 100;
168         EXPECT_FALSE(repo.hasPEL(id));
169 
170         // Try to remove it anyway
171         EXPECT_FALSE(repo.remove(id));
172     }
173 
174     {
175         // Restore and check they're still there, then
176         // remove them.
177         Repository repo{repoPath};
178         EXPECT_TRUE(repo.hasPEL(ids[0]));
179         EXPECT_TRUE(repo.hasPEL(ids[1]));
180 
181         repo.remove(ids[0]);
182         EXPECT_FALSE(repo.hasPEL(ids[0]));
183 
184         repo.remove(ids[1]);
185         EXPECT_FALSE(repo.hasPEL(ids[1]));
186     }
187 }
188 
TEST_F(RepositoryTest,TestGetPELData)189 TEST_F(RepositoryTest, TestGetPELData)
190 {
191     using ID = Repository::LogID;
192     Repository repo{repoPath};
193 
194     ID badID{ID::Pel(42)};
195     auto noData = repo.getPELData(badID);
196     EXPECT_FALSE(noData);
197 
198     // Add a PEL to the repo, and get the data back with getPELData.
199     auto data = pelDataFactory(TestPELType::pelSimple);
200     auto dataCopy = data;
201     auto pel = std::make_unique<PEL>(data);
202     auto pelID = pel->id();
203     repo.add(pel);
204 
205     ID id{ID::Pel(pelID)};
206     auto pelData = repo.getPELData(id);
207 
208     ASSERT_TRUE(pelData);
209     EXPECT_EQ(dataCopy, *pelData);
210 }
211 
TEST_F(RepositoryTest,TestForEach)212 TEST_F(RepositoryTest, TestForEach)
213 {
214     Repository repo{repoPath};
215 
216     // Add 2 PELs
217     auto data = pelDataFactory(TestPELType::pelSimple);
218     auto pel = std::make_unique<PEL>(data);
219     repo.add(pel);
220 
221     pel = std::make_unique<PEL>(data);
222     pel->assignID();
223     pel->setCommitTime();
224     repo.add(pel);
225 
226     // Make a function that saves the IDs
227     std::vector<uint32_t> ids;
228     Repository::ForEachFunc f1 = [&ids](const PEL& pel) {
229         ids.push_back(pel.id());
230         return false;
231     };
232 
233     repo.for_each(f1);
234 
235     EXPECT_EQ(ids.size(), 2);
236 
237     // Stop after the first time in.
238     Repository::ForEachFunc f2 = [&ids](const PEL& pel) {
239         ids.push_back(pel.id());
240         return true;
241     };
242 
243     ids.clear();
244     repo.for_each(f2);
245     EXPECT_EQ(ids.size(), 1);
246 }
247 
TEST_F(RepositoryTest,TestSubscriptions)248 TEST_F(RepositoryTest, TestSubscriptions)
249 {
250     std::vector<uint32_t> added;
251     std::vector<uint32_t> removed;
252 
253     Repository::AddCallback ac = [&added](const PEL& pel) {
254         added.push_back(pel.id());
255     };
256 
257     Repository::DeleteCallback dc = [&removed](uint32_t id) {
258         removed.push_back(id);
259     };
260 
261     Repository repo{repoPath};
262     repo.subscribeToAdds("test", ac);
263     repo.subscribeToDeletes("test", dc);
264 
265     auto data = pelDataFactory(TestPELType::pelSimple);
266     auto pel = std::make_unique<PEL>(data);
267     auto pelID = pel->id();
268     repo.add(pel);
269 
270     EXPECT_EQ(added.size(), 1);
271 
272     using ID = Repository::LogID;
273     ID id{ID::Pel(pelID)};
274     repo.remove(id);
275 
276     EXPECT_EQ(removed.size(), 1);
277 
278     repo.unsubscribeFromAdds("test");
279     repo.unsubscribeFromDeletes("test");
280 
281     added.clear();
282     removed.clear();
283 
284     repo.add(pel);
285     EXPECT_EQ(added.size(), 0);
286 
287     repo.remove(id);
288     EXPECT_EQ(removed.size(), 0);
289 }
290 
TEST_F(RepositoryTest,TestGetAttributes)291 TEST_F(RepositoryTest, TestGetAttributes)
292 {
293     uint32_t pelID = 0;
294     std::bitset<16> actionFlags;
295 
296     {
297         Repository repo{repoPath};
298 
299         // Add a PEL to the repo
300         auto data = pelDataFactory(TestPELType::pelSimple);
301         auto pel = std::make_unique<PEL>(data);
302         repo.add(pel);
303 
304         pelID = pel->id();
305         actionFlags = pel->userHeader().actionFlags();
306 
307         using ID = Repository::LogID;
308         ID id{ID::Pel(pelID)};
309 
310         auto a = repo.getPELAttributes(id);
311         EXPECT_TRUE(a);
312         EXPECT_EQ((*a).get().actionFlags, actionFlags);
313 
314         id.pelID.id = 0;
315         a = repo.getPELAttributes(id);
316         EXPECT_FALSE(a);
317     }
318 
319     {
320         // Restore the repository and check again
321         Repository repo{repoPath};
322 
323         using ID = Repository::LogID;
324         ID id{ID::Pel(pelID)};
325 
326         auto a = repo.getPELAttributes(id);
327         EXPECT_TRUE(a);
328         EXPECT_EQ((*a).get().actionFlags, actionFlags);
329 
330         id.pelID.id = 0;
331         a = repo.getPELAttributes(id);
332         EXPECT_FALSE(a);
333     }
334 }
335 
TEST_F(RepositoryTest,TestSetHostState)336 TEST_F(RepositoryTest, TestSetHostState)
337 {
338     // Add a PEL to the repo
339     auto data = pelDataFactory(TestPELType::pelSimple);
340     auto pel = std::make_unique<PEL>(data);
341     using ID = Repository::LogID;
342     ID id{ID::Pel(pel->id())};
343 
344     {
345         Repository repo{repoPath};
346 
347         repo.add(pel);
348 
349         auto a = repo.getPELAttributes(id);
350         EXPECT_EQ((*a).get().hostState, TransmissionState::newPEL);
351 
352         repo.setPELHostTransState(pel->id(), TransmissionState::acked);
353 
354         // First, check the attributes
355         a = repo.getPELAttributes(id);
356         EXPECT_EQ((*a).get().hostState, TransmissionState::acked);
357 
358         // Next, check the PEL data itself
359         auto pelData = repo.getPELData(id);
360         PEL newPEL{*pelData};
361         EXPECT_EQ(newPEL.hostTransmissionState(), TransmissionState::acked);
362     }
363 
364     {
365         // Now restore, and check again
366         Repository repo{repoPath};
367 
368         // First, check the attributes
369         auto a = repo.getPELAttributes(id);
370         EXPECT_EQ((*a).get().hostState, TransmissionState::acked);
371 
372         // Next, check the PEL data itself
373         auto pelData = repo.getPELData(id);
374         PEL newPEL{*pelData};
375         EXPECT_EQ(newPEL.hostTransmissionState(), TransmissionState::acked);
376     }
377 }
378 
TEST_F(RepositoryTest,TestSetHMCState)379 TEST_F(RepositoryTest, TestSetHMCState)
380 {
381     // Add a PEL to the repo
382     auto data = pelDataFactory(TestPELType::pelSimple);
383     auto pel = std::make_unique<PEL>(data);
384     using ID = Repository::LogID;
385     ID id{ID::Pel(pel->id())};
386 
387     {
388         Repository repo{repoPath};
389 
390         repo.add(pel);
391 
392         auto a = repo.getPELAttributes(id);
393         EXPECT_EQ((*a).get().hmcState, TransmissionState::newPEL);
394 
395         repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
396 
397         // First, check the attributes
398         a = repo.getPELAttributes(id);
399         EXPECT_EQ((*a).get().hmcState, TransmissionState::acked);
400 
401         // Next, check the PEL data itself
402         auto pelData = repo.getPELData(id);
403         PEL newPEL{*pelData};
404         EXPECT_EQ(newPEL.hmcTransmissionState(), TransmissionState::acked);
405     }
406 
407     {
408         // Now restore, and check again
409         Repository repo{repoPath};
410 
411         // First, check the attributes
412         auto a = repo.getPELAttributes(id);
413         EXPECT_EQ((*a).get().hmcState, TransmissionState::acked);
414 
415         // Next, check the PEL data itself
416         auto pelData = repo.getPELData(id);
417         PEL newPEL{*pelData};
418         EXPECT_EQ(newPEL.hmcTransmissionState(), TransmissionState::acked);
419     }
420 }
421 
TEST_F(RepositoryTest,TestGetPELFD)422 TEST_F(RepositoryTest, TestGetPELFD)
423 {
424     Repository repo{repoPath};
425 
426     auto data = pelDataFactory(TestPELType::pelSimple);
427     auto pel = std::make_unique<PEL>(data);
428     pel->setCommitTime();
429     pel->assignID();
430 
431     repo.add(pel);
432 
433     using ID = Repository::LogID;
434     ID id{ID::Pel(pel->id())};
435 
436     auto fd = repo.getPELFD(id);
437 
438     EXPECT_TRUE(fd);
439 
440     // Get the size
441     struct stat s;
442     int r = fstat(*fd, &s);
443     ASSERT_EQ(r, 0);
444 
445     auto size = s.st_size;
446 
447     // Read the PEL data out of the FD
448     FILE* fp = fdopen(*fd, "r");
449     ASSERT_NE(fp, nullptr);
450 
451     std::vector<uint8_t> newData;
452     newData.resize(size);
453     r = fread(newData.data(), 1, size, fp);
454     EXPECT_EQ(r, size);
455 
456     PEL newPEL{newData};
457 
458     EXPECT_TRUE(newPEL.valid());
459     EXPECT_EQ(newPEL.id(), pel->id());
460 
461     fclose(fp);
462 
463     // Call getPELFD again, this time with a bad ID
464     id.pelID.id = 42;
465     fd = repo.getPELFD(id);
466 
467     EXPECT_FALSE(fd);
468 }
469 
470 // Test the repo size statistics
TEST_F(RepositoryTest,TestRepoSizes)471 TEST_F(RepositoryTest, TestRepoSizes)
472 {
473     uint32_t id = 1;
474 
475     Repository repo{repoPath, 10000, 500};
476 
477     // All of the size stats are the sizes on disk a PEL takes up,
478     // which is different than the file size.  Disk usage seems
479     // to have a granularity of 4096 bytes.  This probably shouldn't
480     // be hardcoded, but I don't know how to look it up dynamically.
481 
482     // All sizes are zero
483     {
484         const auto& stats = repo.getSizeStats();
485         EXPECT_EQ(stats.total, 0);
486         EXPECT_EQ(stats.bmc, 0);
487         EXPECT_EQ(stats.nonBMC, 0);
488         EXPECT_EQ(stats.bmcServiceable, 0);
489         EXPECT_EQ(stats.bmcInfo, 0);
490         EXPECT_EQ(stats.nonBMCServiceable, 0);
491         EXPECT_EQ(stats.nonBMCInfo, 0);
492     }
493 
494     // Add a 2000B BMC predictive error
495     auto data = pelFactory(id++, 'O', 0x20, 0x8800, 2000);
496     auto pel = std::make_unique<PEL>(data);
497     auto pelID1 = pel->id();
498     repo.add(pel);
499 
500     {
501         const auto& stats = repo.getSizeStats();
502         EXPECT_EQ(stats.total, 4096);
503         EXPECT_EQ(stats.bmc, 4096);
504         EXPECT_EQ(stats.nonBMC, 0);
505         EXPECT_EQ(stats.bmcServiceable, 4096);
506         EXPECT_EQ(stats.bmcInfo, 0);
507         EXPECT_EQ(stats.nonBMCServiceable, 0);
508         EXPECT_EQ(stats.nonBMCInfo, 0);
509     }
510 
511     // Add a 5000B BMC informational error
512     data = pelFactory(id++, 'O', 0x00, 0x8800, 5000);
513     pel = std::make_unique<PEL>(data);
514     auto pelID2 = pel->id();
515     repo.add(pel);
516 
517     {
518         const auto& stats = repo.getSizeStats();
519         EXPECT_EQ(stats.total, 4096 + 8192);
520         EXPECT_EQ(stats.bmc, 4096 + 8192);
521         EXPECT_EQ(stats.nonBMC, 0);
522         EXPECT_EQ(stats.bmcServiceable, 4096);
523         EXPECT_EQ(stats.bmcInfo, 8192);
524         EXPECT_EQ(stats.nonBMCServiceable, 0);
525         EXPECT_EQ(stats.nonBMCInfo, 0);
526     }
527 
528     // Add a 4000B Hostboot unrecoverable error
529     data = pelFactory(id++, 'B', 0x40, 0x8800, 4000);
530     pel = std::make_unique<PEL>(data);
531     auto pelID3 = pel->id();
532     repo.add(pel);
533 
534     {
535         const auto& stats = repo.getSizeStats();
536         EXPECT_EQ(stats.total, 4096 + 8192 + 4096);
537         EXPECT_EQ(stats.bmc, 4096 + 8192);
538         EXPECT_EQ(stats.nonBMC, 4096);
539         EXPECT_EQ(stats.bmcServiceable, 4096);
540         EXPECT_EQ(stats.bmcInfo, 8192);
541         EXPECT_EQ(stats.nonBMCServiceable, 4096);
542         EXPECT_EQ(stats.nonBMCInfo, 0);
543     }
544 
545     // Add a 5000B Hostboot informational error
546     data = pelFactory(id++, 'B', 0x00, 0x8800, 5000);
547     pel = std::make_unique<PEL>(data);
548     auto pelID4 = pel->id();
549     repo.add(pel);
550 
551     {
552         const auto& stats = repo.getSizeStats();
553         EXPECT_EQ(stats.total, 4096 + 8192 + 4096 + 8192);
554         EXPECT_EQ(stats.bmc, 4096 + 8192);
555         EXPECT_EQ(stats.nonBMC, 4096 + 8192);
556         EXPECT_EQ(stats.bmcServiceable, 4096);
557         EXPECT_EQ(stats.bmcInfo, 8192);
558         EXPECT_EQ(stats.nonBMCServiceable, 4096);
559         EXPECT_EQ(stats.nonBMCInfo, 8192);
560     }
561 
562     // Remove the BMC serviceable error
563     using ID = Repository::LogID;
564     ID id1{ID::Pel(pelID1)};
565 
566     repo.remove(id1);
567     {
568         const auto& stats = repo.getSizeStats();
569         EXPECT_EQ(stats.total, 8192 + 4096 + 8192);
570         EXPECT_EQ(stats.bmc, 8192);
571         EXPECT_EQ(stats.nonBMC, 4096 + 8192);
572         EXPECT_EQ(stats.bmcServiceable, 0);
573         EXPECT_EQ(stats.bmcInfo, 8192);
574         EXPECT_EQ(stats.nonBMCServiceable, 4096);
575         EXPECT_EQ(stats.nonBMCInfo, 8192);
576     }
577 
578     // Remove the Hostboot informational error
579     ID id4{ID::Pel(pelID4)};
580 
581     repo.remove(id4);
582     {
583         const auto& stats = repo.getSizeStats();
584         EXPECT_EQ(stats.total, 8192 + 4096);
585         EXPECT_EQ(stats.bmc, 8192);
586         EXPECT_EQ(stats.nonBMC, 4096);
587         EXPECT_EQ(stats.bmcServiceable, 0);
588         EXPECT_EQ(stats.bmcInfo, 8192);
589         EXPECT_EQ(stats.nonBMCServiceable, 4096);
590         EXPECT_EQ(stats.nonBMCInfo, 0);
591     }
592 
593     // Remove the BMC informational error
594     ID id2{ID::Pel(pelID2)};
595 
596     repo.remove(id2);
597     {
598         const auto& stats = repo.getSizeStats();
599         EXPECT_EQ(stats.total, 4096);
600         EXPECT_EQ(stats.bmc, 0);
601         EXPECT_EQ(stats.nonBMC, 4096);
602         EXPECT_EQ(stats.bmcServiceable, 0);
603         EXPECT_EQ(stats.bmcInfo, 0);
604         EXPECT_EQ(stats.nonBMCServiceable, 4096);
605         EXPECT_EQ(stats.nonBMCInfo, 0);
606     }
607 
608     // Remove the hostboot unrecoverable error
609     ID id3{ID::Pel(pelID3)};
610 
611     repo.remove(id3);
612     {
613         const auto& stats = repo.getSizeStats();
614         EXPECT_EQ(stats.total, 0);
615         EXPECT_EQ(stats.bmc, 0);
616         EXPECT_EQ(stats.nonBMC, 0);
617         EXPECT_EQ(stats.bmcServiceable, 0);
618         EXPECT_EQ(stats.bmcInfo, 0);
619         EXPECT_EQ(stats.nonBMCServiceable, 0);
620         EXPECT_EQ(stats.nonBMCInfo, 0);
621     }
622 }
623 
624 // Prune PELs, when no HMC/OS/PHYP acks
TEST_F(RepositoryTest,TestPruneNoAcks)625 TEST_F(RepositoryTest, TestPruneNoAcks)
626 {
627     std::vector<uint32_t> id;
628     Repository repo{repoPath, 4096 * 20, 100};
629 
630     // Add 10 4096B (on disk) PELs of BMC nonInfo, Info and nonBMC info,
631     // nonInfo errors. None of them acked by PHYP, host, or HMC.
632     for (uint32_t i = 1; i <= 10; i++)
633     {
634         // BMC predictive
635         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
636         auto pel = std::make_unique<PEL>(data);
637         repo.add(pel);
638 
639         // BMC info
640         data = pelFactory(i + 100, 'O', 0x0, 0x8800, 500);
641         pel = std::make_unique<PEL>(data);
642         repo.add(pel);
643 
644         // Hostboot predictive
645         data = pelFactory(i + 200, 'B', 0x20, 0x8800, 500);
646         pel = std::make_unique<PEL>(data);
647         repo.add(pel);
648 
649         // Hostboot info
650         data = pelFactory(i + 300, 'B', 0x0, 0x8800, 500);
651         pel = std::make_unique<PEL>(data);
652         repo.add(pel);
653     }
654 
655     const auto& sizes = repo.getSizeStats();
656     EXPECT_EQ(sizes.total, 4096 * 40);
657 
658     // Sanity check the very first PELs with IDs 1 to 4 are
659     // there so we can check they are removed after the prune.
660     for (uint32_t i = 1; i < 5; i++)
661     {
662         Repository::LogID logID{Repository::LogID::Pel{i}};
663         EXPECT_TRUE(repo.getPELAttributes(logID));
664     }
665 
666     // Prune down to 15%/30%/15%/30% = 90% total
667     auto IDs = repo.prune(id);
668 
669     // Check the final sizes
670     EXPECT_EQ(sizes.total, 4096 * 18);            // 90% of 20 PELs
671     EXPECT_EQ(sizes.bmcInfo, 4096 * 3);           // 15% of 20 PELs
672     EXPECT_EQ(sizes.bmcServiceable, 4096 * 6);    // 30% of 20 PELs
673     EXPECT_EQ(sizes.nonBMCInfo, 4096 * 3);        // 15% of 20 PELs
674     EXPECT_EQ(sizes.nonBMCServiceable, 4096 * 6); // 30% of 20 PELs
675 
676     // Check that at least the 4 oldest, which are the oldest of
677     // each type, were removed.
678     for (uint32_t i = 1; i < 5; i++)
679     {
680         Repository::LogID logID{Repository::LogID::Pel{i}};
681         EXPECT_FALSE(repo.getPELAttributes(logID));
682 
683         // Make sure the corresponding OpenBMC event log ID which is
684         // 500 + the PEL ID is in the list.
685         EXPECT_TRUE(std::find(IDs.begin(), IDs.end(), 500 + i) != IDs.end());
686     }
687 }
688 
689 // Test that if filled completely with 1 type of PEL, that
690 // pruning still works properly
TEST_F(RepositoryTest,TestPruneInfoOnly)691 TEST_F(RepositoryTest, TestPruneInfoOnly)
692 {
693     std::vector<uint32_t> id;
694     Repository repo{repoPath, 4096 * 22, 100};
695 
696     // Fill 4096*23 bytes on disk of BMC info PELs
697     for (uint32_t i = 1; i <= 23; i++)
698     {
699         auto data = pelFactory(i, 'O', 0, 0x8800, 1000);
700         auto pel = std::make_unique<PEL>(data);
701         repo.add(pel);
702     }
703 
704     const auto& sizes = repo.getSizeStats();
705     EXPECT_EQ(sizes.total, 4096 * 23);
706 
707     // Pruning to 15% of 4096 * 22 will leave 3 4096B PELs.
708 
709     // Sanity check the oldest 20 are there so when they
710     // get pruned below we'll know they were removed.
711     for (uint32_t i = 1; i <= 20; i++)
712     {
713         Repository::LogID logID{Repository::LogID::Pel{i}};
714         EXPECT_TRUE(repo.getPELAttributes(logID));
715     }
716 
717     auto IDs = repo.prune(id);
718 
719     // Check the final sizes
720     EXPECT_EQ(sizes.total, 4096 * 3);
721     EXPECT_EQ(sizes.bmcInfo, 4096 * 3);
722     EXPECT_EQ(sizes.bmcServiceable, 0);
723     EXPECT_EQ(sizes.nonBMCInfo, 0);
724     EXPECT_EQ(sizes.nonBMCServiceable, 0);
725 
726     EXPECT_EQ(IDs.size(), 20);
727 
728     // Can no longer find the oldest 20 PELs.
729     for (uint32_t i = 1; i <= 20; i++)
730     {
731         Repository::LogID logID{Repository::LogID::Pel{i}};
732         EXPECT_FALSE(repo.getPELAttributes(logID));
733         EXPECT_TRUE(std::find(IDs.begin(), IDs.end(), 500 + i) != IDs.end());
734     }
735 }
736 
737 // Test that the HMC/OS/PHYP ack values affect the
738 // pruning order.
TEST_F(RepositoryTest,TestPruneWithAcks)739 TEST_F(RepositoryTest, TestPruneWithAcks)
740 {
741     std::vector<uint32_t> id;
742     Repository repo{repoPath, 4096 * 20, 100};
743 
744     // Fill 30% worth of BMC non-info non-acked PELs
745     for (uint32_t i = 1; i <= 6; i++)
746     {
747         // BMC predictive
748         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
749         auto pel = std::make_unique<PEL>(data);
750         repo.add(pel);
751     }
752 
753     // Add another PEL to push it over the 30%, each time adding
754     // a different type that should be pruned before the above ones
755     // even though those are older.
756     for (uint32_t i = 1; i <= 3; i++)
757     {
758         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
759         auto pel = std::make_unique<PEL>(data);
760         auto idToDelete = pel->obmcLogID();
761         repo.add(pel);
762 
763         if (1 == i)
764         {
765             repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
766         }
767         else if (2 == i)
768         {
769             repo.setPELHostTransState(pel->id(), TransmissionState::acked);
770         }
771         else
772         {
773             repo.setPELHostTransState(pel->id(), TransmissionState::sent);
774         }
775 
776         auto IDs = repo.prune(id);
777         EXPECT_EQ(repo.getSizeStats().total, 4096 * 6);
778 
779         // The newest PEL should be the one deleted
780         ASSERT_EQ(IDs.size(), 1);
781         EXPECT_EQ(IDs[0], idToDelete);
782     }
783 }
784 
785 // Test that the total number of PELs limit is enforced.
TEST_F(RepositoryTest,TestPruneTooManyPELs)786 TEST_F(RepositoryTest, TestPruneTooManyPELs)
787 {
788     std::vector<uint32_t> id;
789     Repository repo{repoPath, 4096 * 100, 10};
790 
791     // Add 10, which is the limit and is still OK
792     for (uint32_t i = 1; i <= 10; i++)
793     {
794         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
795         auto pel = std::make_unique<PEL>(data);
796         repo.add(pel);
797     }
798 
799     auto IDs = repo.prune(id);
800 
801     // Nothing pruned yet
802     EXPECT_TRUE(IDs.empty());
803 
804     // Add 1 more PEL which will be too many.
805     {
806         auto data = pelFactory(11, 'O', 0x20, 0x8800, 500);
807         auto pel = std::make_unique<PEL>(data);
808         repo.add(pel);
809     }
810 
811     // Now that's it's over the limit of 10, it will bring it down
812     // to 80%, which is 8 after it removes 3.
813     IDs = repo.prune(id);
814     EXPECT_EQ(repo.getSizeStats().total, 4096 * 8);
815     ASSERT_EQ(IDs.size(), 3);
816 
817     // Check that it deleted the oldest ones.
818     // The OpenBMC log ID is the PEL ID + 500.
819     EXPECT_EQ(IDs[0], 500 + 1);
820     EXPECT_EQ(IDs[1], 500 + 2);
821     EXPECT_EQ(IDs[2], 500 + 3);
822 }
823 
824 // Test the sizeWarning function
TEST_F(RepositoryTest,TestSizeWarning)825 TEST_F(RepositoryTest, TestSizeWarning)
826 {
827     uint32_t id = 1;
828     Repository repo{repoPath, 100 * 4096, 500};
829 
830     EXPECT_FALSE(repo.sizeWarning());
831 
832     // 95% is still OK (disk size for these is 4096)
833     for (uint32_t i = 1; i <= 95; i++)
834     {
835         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
836         auto pel = std::make_unique<PEL>(data);
837         repo.add(pel);
838     }
839 
840     EXPECT_FALSE(repo.sizeWarning());
841 
842     // Now at 96%
843     auto data = pelFactory(id++, 'B', 0x20, 0x8800, 500);
844     auto pel = std::make_unique<PEL>(data);
845     repo.add(pel);
846 
847     EXPECT_TRUE(repo.sizeWarning());
848 }
849 
850 // Test sizeWarning when there are too many PEls
TEST_F(RepositoryTest,TestSizeWarningNumPELs)851 TEST_F(RepositoryTest, TestSizeWarningNumPELs)
852 {
853     Repository repo{repoPath, 4096 * 100, 5};
854 
855     EXPECT_FALSE(repo.sizeWarning());
856 
857     for (uint32_t i = 1; i <= 5; i++)
858     {
859         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
860         auto pel = std::make_unique<PEL>(data);
861         repo.add(pel);
862     }
863 
864     EXPECT_FALSE(repo.sizeWarning());
865 
866     // Add 1 more for a total of 6, now over the limit
867     {
868         auto data = pelFactory(6, 'O', 0x20, 0x8800, 500);
869         auto pel = std::make_unique<PEL>(data);
870         repo.add(pel);
871     }
872 
873     EXPECT_TRUE(repo.sizeWarning());
874 }
875 
876 // Test existense of archive file
TEST_F(RepositoryTest,TestArchiveFile)877 TEST_F(RepositoryTest, TestArchiveFile)
878 {
879     using pelID = Repository::LogID::Pel;
880     using obmcID = Repository::LogID::Obmc;
881 
882     // Add and remove a PEL from the repo
883 
884     Repository repo{repoPath};
885 
886     fs::path archivePath = repoPath / "logs" / "archive";
887     EXPECT_TRUE(fs::exists(archivePath));
888 
889     auto data = pelDataFactory(TestPELType::pelSimple);
890     auto pel = std::make_unique<PEL>(data, 1);
891 
892     pel->assignID();
893     Repository::LogID id{pelID{pel->id()}, obmcID{pel->obmcLogID()}};
894 
895     repo.add(pel);
896 
897     auto path = repoPath / "logs" /
898                 Repository::getPELFilename(pel->id(), pel->commitTime());
899     EXPECT_TRUE(fs::exists(path));
900 
901     auto removedID = repo.remove(id);
902     ASSERT_TRUE(removedID);
903     EXPECT_EQ(*removedID, id);
904 
905     archivePath /= Repository::getPELFilename(pel->id(), pel->commitTime());
906     EXPECT_TRUE(fs::exists(archivePath));
907 
908     EXPECT_FALSE(repo.hasPEL(id));
909 }
910 
911 // Test archive folder size with sizeWarning function
TEST_F(RepositoryTest,TestArchiveSize)912 TEST_F(RepositoryTest, TestArchiveSize)
913 {
914     using pelID = Repository::LogID::Pel;
915     using obmcID = Repository::LogID::Obmc;
916 
917     // Create repo with max PEL=500 and space=4096*100
918     Repository repo{repoPath, 100 * 4096, 500};
919 
920     // Fill 94% (disk size for these is 4096)
921     for (uint32_t i = 1; i <= 94; i++)
922     {
923         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
924         auto pel = std::make_unique<PEL>(data);
925         repo.add(pel);
926     }
927 
928     // Add another PEL which makes 95% still ok
929     auto data = pelDataFactory(TestPELType::pelSimple);
930     auto pel = std::make_unique<PEL>(data, 1);
931     pel->assignID();
932     Repository::LogID id{pelID{pel->id()}, obmcID{pel->obmcLogID()}};
933     repo.add(pel);
934 
935     // With 95% full expect no size warning
936     EXPECT_FALSE(repo.sizeWarning());
937 
938     // Remove last created PEL
939     repo.remove(id);
940 
941     // Repo is 94% full with one PEL in archive log
942     // Total repo size 95% full (including archive) still ok
943     EXPECT_FALSE(repo.sizeWarning());
944 
945     // Confirm the repo size 94% full
946     const auto& sizes = repo.getSizeStats();
947     EXPECT_EQ(sizes.total, 4096 * 94);
948 
949     // Make sure archive contain the one deleted file
950     fs::path archivePath = repoPath / "logs" / "archive";
951     archivePath /= Repository::getPELFilename(pel->id(), pel->commitTime());
952     EXPECT_TRUE(fs::exists(archivePath));
953 
954     // Add another PEL which makes repo 95% full
955     data = pelDataFactory(TestPELType::pelSimple);
956     pel = std::make_unique<PEL>(data, 1);
957     pel->assignID();
958     Repository::LogID idx{pelID{pel->id()}, obmcID{pel->obmcLogID()}};
959     repo.add(pel);
960 
961     // Repo with 95% full + one archive file becomes 96%
962     // which is greater than the warning
963     // expect archive file to be deleted to get repo size back to 95%
964     EXPECT_FALSE(repo.sizeWarning());
965     EXPECT_FALSE(fs::exists(archivePath));
966 }
967 
TEST_F(RepositoryTest,GetLogIDFoundTC)968 TEST_F(RepositoryTest, GetLogIDFoundTC)
969 {
970     // Add and Check the created LogId
971 
972     Repository repo{repoPath};
973     auto data = pelDataFactory(TestPELType::pelSimple);
974     auto pel = std::make_unique<PEL>(data, 1);
975 
976     pel->assignID();
977 
978     repo.add(pel);
979 
980     // Getting by PEL Id
981     Repository::LogID idWithPelId{Repository::LogID::Pel(pel->id())};
982     auto logID = repo.getLogID(idWithPelId);
983     ASSERT_TRUE(logID.has_value());
984     EXPECT_EQ(logID->obmcID.id, pel->obmcLogID());
985     EXPECT_EQ(logID->pelID.id, pel->id());
986 
987     // Getting by OBMC Event Log Id
988     Repository::LogID idWithObmcLogId{
989         Repository::LogID::Obmc(pel->obmcLogID())};
990     logID = repo.getLogID(idWithObmcLogId);
991     ASSERT_TRUE(logID.has_value());
992     EXPECT_EQ(logID->obmcID.id, pel->obmcLogID());
993     EXPECT_EQ(logID->pelID.id, pel->id());
994 }
995 
TEST_F(RepositoryTest,GetLogIDNotFoundTC)996 TEST_F(RepositoryTest, GetLogIDNotFoundTC)
997 {
998     // Add and Check the created LogId
999 
1000     Repository repo{repoPath};
1001     auto data = pelDataFactory(TestPELType::pelSimple);
1002     auto pel = std::make_unique<PEL>(data, 1);
1003 
1004     pel->assignID();
1005 
1006     repo.add(pel);
1007 
1008     // Getting by invalid PEL Id
1009     Repository::LogID idWithPelId{Repository::LogID::Pel(0xFFFFFFFF)};
1010     auto logID = repo.getLogID(idWithPelId);
1011     ASSERT_TRUE(!logID.has_value());
1012 
1013     // Getting by invalid OBMC Event Log ID
1014     Repository::LogID idWithObmcLogId{Repository::LogID::Obmc(0xFFFFFFFF)};
1015     logID = repo.getLogID(idWithObmcLogId);
1016     ASSERT_TRUE(!logID.has_value());
1017 }
1018 
1019 // Test that OpenBMC log Id with hardware isolation entry is not removed.
TEST_F(RepositoryTest,TestPruneWithIdHwIsoEntry)1020 TEST_F(RepositoryTest, TestPruneWithIdHwIsoEntry)
1021 {
1022     std::vector<uint32_t> id{502};
1023     Repository repo{repoPath, 4096 * 100, 10};
1024 
1025     // Add 10, which is the limit and is still OK
1026     for (uint32_t i = 1; i <= 10; i++)
1027     {
1028         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
1029         auto pel = std::make_unique<PEL>(data);
1030         repo.add(pel);
1031     }
1032 
1033     auto IDs = repo.prune(id);
1034 
1035     // Nothing pruned yet
1036     EXPECT_TRUE(IDs.empty());
1037 
1038     // Add 1 more PEL which will be too many.
1039     {
1040         auto data = pelFactory(11, 'O', 0x20, 0x8800, 500);
1041         auto pel = std::make_unique<PEL>(data);
1042         repo.add(pel);
1043     }
1044 
1045     // Now that's it's over the limit of 10, it will bring it down
1046     // to 80%, which is 8 after it removes 3.
1047     IDs = repo.prune(id);
1048     EXPECT_EQ(repo.getSizeStats().total, 4096 * 8);
1049     ASSERT_EQ(IDs.size(), 3);
1050 
1051     // Check that it deleted the oldest ones.
1052     // And the Id with hw isolation entry is NOT removed.
1053     // The OpenBMC log ID is the PEL ID + 500.
1054     EXPECT_EQ(IDs[0], 500 + 1);
1055     EXPECT_EQ(IDs[1], 500 + 3);
1056     EXPECT_EQ(IDs[2], 500 + 4);
1057 }
1058