xref: /openbmc/phosphor-logging/test/openpower-pels/repository_test.cpp (revision be952d2edc63d9c9ad6ed389e012043b345e483e)
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