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