blob: da3f2c6a5cf0e560039495623b424dbba3588b64 [file] [log] [blame]
Matt Spinler97f7abc2019-11-06 09:40:23 -06001/**
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 */
Matt Spinler89fa0822019-07-17 13:54:30 -050016#include "extensions/openpower-pels/manager.hpp"
17#include "log_manager.hpp"
Matt Spinlere6b48f12020-04-02 09:51:39 -050018#include "mocks.hpp"
Matt Spinler89fa0822019-07-17 13:54:30 -050019#include "pel_utils.hpp"
20
Matt Spinlere6b48f12020-04-02 09:51:39 -050021#include <sdbusplus/test/sdbus_mock.hpp>
Matt Spinlera34ab722019-12-16 10:39:32 -060022#include <xyz/openbmc_project/Common/error.hpp>
Matt Spinler89fa0822019-07-17 13:54:30 -050023
Patrick Williams2544b412022-10-04 08:41:06 -050024#include <fstream>
25#include <regex>
26
Matt Spinler89fa0822019-07-17 13:54:30 -050027#include <gtest/gtest.h>
28
29using namespace openpower::pels;
30namespace fs = std::filesystem;
31
Matt Spinlere6b48f12020-04-02 09:51:39 -050032using ::testing::NiceMock;
Matt Spinler3dd17e92020-08-05 15:04:27 -050033using ::testing::Return;
Matt Spinlere6b48f12020-04-02 09:51:39 -050034
Matt Spinler05c2c6c2019-12-18 14:02:09 -060035class TestLogger
36{
37 public:
38 void log(const std::string& name, phosphor::logging::Entry::Level level,
39 const EventLogger::ADMap& additionalData)
40 {
41 errName = name;
42 errLevel = level;
43 ad = additionalData;
44 }
45
46 std::string errName;
47 phosphor::logging::Entry::Level errLevel;
48 EventLogger::ADMap ad;
49};
50
Matt Spinler89fa0822019-07-17 13:54:30 -050051class ManagerTest : public CleanPELFiles
52{
Matt Spinler6b1a5c82020-01-07 08:48:53 -060053 public:
Matt Spinlere6b48f12020-04-02 09:51:39 -050054 ManagerTest() :
55 bus(sdbusplus::get_mocked_new(&sdbusInterface)),
56 logManager(bus, "logging_path")
Matt Spinler6b1a5c82020-01-07 08:48:53 -060057 {
58 sd_event_default(&sdEvent);
Matt Spinler6b1a5c82020-01-07 08:48:53 -060059 }
60
Matt Spinler32e36b82023-04-25 10:57:15 -050061 fs::path makeTempDir()
62 {
63 char path[] = "/tmp/tempnameXXXXXX";
64 std::filesystem::path dir = mkdtemp(path);
65 dirsToRemove.push_back(dir);
66 return dir;
67 }
68
Matt Spinler6b1a5c82020-01-07 08:48:53 -060069 ~ManagerTest()
70 {
Matt Spinler32e36b82023-04-25 10:57:15 -050071 for (const auto& d : dirsToRemove)
72 {
73 std::filesystem::remove_all(d);
74 }
Matt Spinler6b1a5c82020-01-07 08:48:53 -060075 sd_event_unref(sdEvent);
76 }
77
Matt Spinlere6b48f12020-04-02 09:51:39 -050078 NiceMock<sdbusplus::SdBusMock> sdbusInterface;
Patrick Williams45e83522022-07-22 19:26:52 -050079 sdbusplus::bus_t bus;
Matt Spinler6b1a5c82020-01-07 08:48:53 -060080 phosphor::logging::internal::Manager logManager;
81 sd_event* sdEvent;
Matt Spinler05c2c6c2019-12-18 14:02:09 -060082 TestLogger logger;
Matt Spinler32e36b82023-04-25 10:57:15 -050083 std::vector<std::filesystem::path> dirsToRemove;
Matt Spinler89fa0822019-07-17 13:54:30 -050084};
85
Matt Spinler67456c22019-10-21 12:22:49 -050086std::optional<fs::path> findAnyPELInRepo()
87{
88 // PELs are named <timestamp>_<ID>
89 std::regex expr{"\\d+_\\d+"};
90
91 for (auto& f : fs::directory_iterator(getPELRepoPath() / "logs"))
92 {
93 if (std::regex_search(f.path().string(), expr))
94 {
95 return f.path();
96 }
97 }
98 return std::nullopt;
99}
100
Matt Spinler7e727a32020-07-07 15:00:17 -0500101size_t countPELsInRepo()
102{
103 size_t count = 0;
104 std::regex expr{"\\d+_\\d+"};
105
106 for (auto& f : fs::directory_iterator(getPELRepoPath() / "logs"))
107 {
108 if (std::regex_search(f.path().string(), expr))
109 {
110 count++;
111 }
112 }
113 return count;
114}
115
Matt Spinlerff9cec22020-07-15 13:06:35 -0500116void deletePELFile(uint32_t id)
117{
118 char search[20];
119
120 sprintf(search, "\\d+_%.8X", id);
121 std::regex expr{search};
122
123 for (auto& f : fs::directory_iterator(getPELRepoPath() / "logs"))
124 {
125 if (std::regex_search(f.path().string(), expr))
126 {
127 fs::remove(f.path());
128 break;
129 }
130 }
131}
132
Matt Spinler89fa0822019-07-17 13:54:30 -0500133// Test that using the RAWPEL=<file> with the Manager::create() call gets
134// a PEL saved in the repository.
135TEST_F(ManagerTest, TestCreateWithPEL)
136{
Matt Spinlerc8705e22019-09-11 12:36:07 -0500137 std::unique_ptr<DataInterfaceBase> dataIface =
Matt Spinlere6b48f12020-04-02 09:51:39 -0500138 std::make_unique<MockDataInterface>();
Matt Spinler89fa0822019-07-17 13:54:30 -0500139
Matt Spinlerd96fa602022-12-15 11:11:26 -0600140 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
141
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600142 openpower::pels::Manager manager{
143 logManager, std::move(dataIface),
144 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
Matt Spinlerd96fa602022-12-15 11:11:26 -0600145 std::placeholders::_2, std::placeholders::_3),
146 std::move(journal)};
Matt Spinler89fa0822019-07-17 13:54:30 -0500147
148 // Create a PEL, write it to a file, and pass that filename into
149 // the create function.
Matt Spinler42828bd2019-10-11 10:39:30 -0500150 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinler89fa0822019-07-17 13:54:30 -0500151
152 fs::path pelFilename = makeTempDir() / "rawpel";
153 std::ofstream pelFile{pelFilename};
Matt Spinler42828bd2019-10-11 10:39:30 -0500154 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
Matt Spinler89fa0822019-07-17 13:54:30 -0500155 pelFile.close();
156
157 std::string adItem = "RAWPEL=" + pelFilename.string();
158 std::vector<std::string> additionalData{adItem};
159 std::vector<std::string> associations;
160
Matt Spinler367144c2019-09-19 15:33:52 -0500161 manager.create("error message", 42, 0,
162 phosphor::logging::Entry::Level::Error, additionalData,
Matt Spinler89fa0822019-07-17 13:54:30 -0500163 associations);
164
Matt Spinler67456c22019-10-21 12:22:49 -0500165 // Find the file in the PEL repository directory
166 auto pelPathInRepo = findAnyPELInRepo();
Matt Spinler89fa0822019-07-17 13:54:30 -0500167
Matt Spinler67456c22019-10-21 12:22:49 -0500168 EXPECT_TRUE(pelPathInRepo);
Matt Spinler89fa0822019-07-17 13:54:30 -0500169
Matt Spinler475e5742019-07-18 16:09:49 -0500170 // Now remove it based on its OpenBMC event log ID
171 manager.erase(42);
172
Matt Spinler67456c22019-10-21 12:22:49 -0500173 pelPathInRepo = findAnyPELInRepo();
Matt Spinler475e5742019-07-18 16:09:49 -0500174
Matt Spinler67456c22019-10-21 12:22:49 -0500175 EXPECT_FALSE(pelPathInRepo);
Matt Spinler89fa0822019-07-17 13:54:30 -0500176}
Matt Spinler67456c22019-10-21 12:22:49 -0500177
Matt Spinlere95fd012020-01-07 12:53:16 -0600178TEST_F(ManagerTest, TestCreateWithInvalidPEL)
179{
180 std::unique_ptr<DataInterfaceBase> dataIface =
Matt Spinlere6b48f12020-04-02 09:51:39 -0500181 std::make_unique<MockDataInterface>();
Matt Spinlere95fd012020-01-07 12:53:16 -0600182
Matt Spinlerd96fa602022-12-15 11:11:26 -0600183 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
184
Matt Spinlere95fd012020-01-07 12:53:16 -0600185 openpower::pels::Manager manager{
186 logManager, std::move(dataIface),
187 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
Matt Spinlerd96fa602022-12-15 11:11:26 -0600188 std::placeholders::_2, std::placeholders::_3),
189 std::move(journal)};
Matt Spinlere95fd012020-01-07 12:53:16 -0600190
191 // Create a PEL, write it to a file, and pass that filename into
192 // the create function.
193 auto data = pelDataFactory(TestPELType::pelSimple);
194
195 // Truncate it to make it invalid.
196 data.resize(200);
197
198 fs::path pelFilename = makeTempDir() / "rawpel";
199 std::ofstream pelFile{pelFilename};
200 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
201 pelFile.close();
202
203 std::string adItem = "RAWPEL=" + pelFilename.string();
204 std::vector<std::string> additionalData{adItem};
205 std::vector<std::string> associations;
206
207 manager.create("error message", 42, 0,
208 phosphor::logging::Entry::Level::Error, additionalData,
209 associations);
210
211 // Run the event loop to log the bad PEL event
212 sdeventplus::Event e{sdEvent};
213 e.run(std::chrono::milliseconds(1));
214
215 PEL invalidPEL{data};
216 EXPECT_EQ(logger.errName, "org.open_power.Logging.Error.BadHostPEL");
217 EXPECT_EQ(logger.errLevel, phosphor::logging::Entry::Level::Error);
218 EXPECT_EQ(std::stoi(logger.ad["PLID"], nullptr, 16), invalidPEL.plid());
219 EXPECT_EQ(logger.ad["OBMC_LOG_ID"], "42");
220 EXPECT_EQ(logger.ad["SRC"], (*invalidPEL.primarySRC())->asciiString());
221 EXPECT_EQ(logger.ad["PEL_SIZE"], std::to_string(data.size()));
222
Matt Spinlerfe721892020-04-02 10:28:08 -0500223 // Check that the bad PEL data was saved to a file.
224 auto badPELData = readPELFile(getPELRepoPath() / "badPEL");
225 EXPECT_EQ(*badPELData, data);
Matt Spinlere95fd012020-01-07 12:53:16 -0600226}
227
Matt Spinler67456c22019-10-21 12:22:49 -0500228// Test that the message registry can be used to build a PEL.
229TEST_F(ManagerTest, TestCreateWithMessageRegistry)
230{
231 const auto registry = R"(
232{
233 "PELs":
234 [
235 {
236 "Name": "xyz.openbmc_project.Error.Test",
237 "Subsystem": "power_supply",
238 "ActionFlags": ["service_action", "report"],
239 "SRC":
240 {
241 "ReasonCode": "0x2030"
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800242 },
Vijay Lobo593a4c62021-06-16 14:25:26 -0500243 "Callouts": [
244 {
245 "CalloutList": [
246 {"Priority": "high", "Procedure": "bmc_code"},
247 {"Priority": "medium", "SymbolicFRU": "service_docs"}
248 ]
249 }
250 ],
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800251 "Documentation":
252 {
253 "Description": "A PGOOD Fault",
254 "Message": "PS had a PGOOD Fault"
Matt Spinler67456c22019-10-21 12:22:49 -0500255 }
Matt Spinler30ddc9f2020-07-16 15:39:59 -0500256 },
257 {
258 "Name": "xyz.openbmc_project.Logging.Error.Default",
259 "Subsystem": "bmc_firmware",
260 "SRC":
261 {
262 "ReasonCode": "0x2031"
263 },
264 "Documentation":
265 {
266 "Description": "The entry used when no match found",
267 "Message": "This is a generic SRC"
268 }
Matt Spinler67456c22019-10-21 12:22:49 -0500269 }
270 ]
271}
272)";
273
Matt Spinler0d804ef2020-05-12 16:16:26 -0500274 auto path = getPELReadOnlyDataPath();
Matt Spinlerd4ffb652019-11-12 14:16:14 -0600275 fs::create_directories(path);
276 path /= "message_registry.json";
277
Matt Spinler67456c22019-10-21 12:22:49 -0500278 std::ofstream registryFile{path};
279 registryFile << registry;
280 registryFile.close();
281
Matt Spinler67456c22019-10-21 12:22:49 -0500282 std::unique_ptr<DataInterfaceBase> dataIface =
Matt Spinlere6b48f12020-04-02 09:51:39 -0500283 std::make_unique<MockDataInterface>();
Matt Spinler67456c22019-10-21 12:22:49 -0500284
Sumit Kumar9d43a722021-08-24 09:46:19 -0500285 MockDataInterface* mockIface =
286 reinterpret_cast<MockDataInterface*>(dataIface.get());
287
288 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
289 "system/entry"};
290 EXPECT_CALL(*mockIface, checkDumpStatus(dumpType))
291 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
292
Matt Spinlerd96fa602022-12-15 11:11:26 -0600293 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
294
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600295 openpower::pels::Manager manager{
296 logManager, std::move(dataIface),
297 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
Matt Spinlerd96fa602022-12-15 11:11:26 -0600298 std::placeholders::_2, std::placeholders::_3),
299 std::move(journal)};
Matt Spinler67456c22019-10-21 12:22:49 -0500300
Matt Spinler30ddc9f2020-07-16 15:39:59 -0500301 std::vector<std::string> additionalData{"FOO=BAR"};
Matt Spinler67456c22019-10-21 12:22:49 -0500302 std::vector<std::string> associations;
303
304 // Create the event log to create the PEL from.
305 manager.create("xyz.openbmc_project.Error.Test", 33, 0,
306 phosphor::logging::Entry::Level::Error, additionalData,
307 associations);
308
309 // Ensure a PEL was created in the repository
310 auto pelFile = findAnyPELInRepo();
311 ASSERT_TRUE(pelFile);
312
313 auto data = readPELFile(*pelFile);
314 PEL pel(*data);
315
316 // Spot check it. Other testcases cover the details.
317 EXPECT_TRUE(pel.valid());
318 EXPECT_EQ(pel.obmcLogID(), 33);
319 EXPECT_EQ(pel.primarySRC().value()->asciiString(),
320 "BD612030 ");
Vijay Lobod354a392021-06-01 16:21:02 -0500321 // Check if the eventId creation is good
322 EXPECT_EQ(manager.getEventId(pel),
323 "BD612030 00000055 00000010 00000000 00000000 00000000 00000000 "
324 "00000000 00000000");
Vijay Lobo593a4c62021-06-16 14:25:26 -0500325 // Check if resolution property creation is good
326 EXPECT_EQ(manager.getResolution(pel),
Matt Spinlerea2873d2021-08-18 10:35:40 -0500327 "1. Priority: High, Procedure: BMC0001\n2. Priority: Medium, PN: "
Vijay Lobo593a4c62021-06-16 14:25:26 -0500328 "SVCDOCS\n");
Matt Spinler67456c22019-10-21 12:22:49 -0500329
330 // Remove it
331 manager.erase(33);
332 pelFile = findAnyPELInRepo();
333 EXPECT_FALSE(pelFile);
334
335 // Create an event log that can't be found in the registry.
Matt Spinler30ddc9f2020-07-16 15:39:59 -0500336 // In this case, xyz.openbmc_project.Logging.Error.Default will
337 // be used as the key instead to find a registry match.
338 manager.create("xyz.openbmc_project.Error.Foo", 42, 0,
Matt Spinler67456c22019-10-21 12:22:49 -0500339 phosphor::logging::Entry::Level::Error, additionalData,
340 associations);
341
Matt Spinler30ddc9f2020-07-16 15:39:59 -0500342 // Ensure a PEL was still created in the repository
Matt Spinler67456c22019-10-21 12:22:49 -0500343 pelFile = findAnyPELInRepo();
Matt Spinler30ddc9f2020-07-16 15:39:59 -0500344 ASSERT_TRUE(pelFile);
345
346 data = readPELFile(*pelFile);
347 PEL newPEL(*data);
348
349 EXPECT_TRUE(newPEL.valid());
350 EXPECT_EQ(newPEL.obmcLogID(), 42);
351 EXPECT_EQ(newPEL.primarySRC().value()->asciiString(),
352 "BD8D2031 ");
353
354 // Check for both the original AdditionalData item as well as
355 // the ERROR_NAME item that should contain the error message
356 // property that wasn't found.
357 std::string errorName;
358 std::string adItem;
359
360 for (const auto& section : newPEL.optionalSections())
361 {
362 if (SectionID::userData == static_cast<SectionID>(section->header().id))
363 {
364 if (UserDataFormat::json ==
365 static_cast<UserDataFormat>(section->header().subType))
366 {
367 auto ud = static_cast<UserData*>(section.get());
368
369 // Check that there was a UserData section added that
370 // contains debug details about the device.
371 const auto& d = ud->data();
372 std::string jsonString{d.begin(), d.end()};
373 auto json = nlohmann::json::parse(jsonString);
374
375 if (json.contains("ERROR_NAME"))
376 {
377 errorName = json["ERROR_NAME"].get<std::string>();
378 }
379
380 if (json.contains("FOO"))
381 {
382 adItem = json["FOO"].get<std::string>();
383 }
384 }
385 }
386 if (!errorName.empty())
387 {
388 break;
389 }
390 }
391
392 EXPECT_EQ(errorName, "xyz.openbmc_project.Error.Foo");
393 EXPECT_EQ(adItem, "BAR");
Matt Spinler67456c22019-10-21 12:22:49 -0500394}
Matt Spinlera34ab722019-12-16 10:39:32 -0600395
396TEST_F(ManagerTest, TestDBusMethods)
397{
Matt Spinlera34ab722019-12-16 10:39:32 -0600398 std::unique_ptr<DataInterfaceBase> dataIface =
Matt Spinlere6b48f12020-04-02 09:51:39 -0500399 std::make_unique<MockDataInterface>();
Matt Spinlera34ab722019-12-16 10:39:32 -0600400
Matt Spinlerd96fa602022-12-15 11:11:26 -0600401 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
402
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600403 Manager manager{logManager, std::move(dataIface),
404 std::bind(std::mem_fn(&TestLogger::log), &logger,
405 std::placeholders::_1, std::placeholders::_2,
Matt Spinlerd96fa602022-12-15 11:11:26 -0600406 std::placeholders::_3),
407 std::move(journal)};
Matt Spinlera34ab722019-12-16 10:39:32 -0600408
409 // Create a PEL, write it to a file, and pass that filename into
410 // the create function so there's one in the repo.
411 auto data = pelDataFactory(TestPELType::pelSimple);
412
413 fs::path pelFilename = makeTempDir() / "rawpel";
414 std::ofstream pelFile{pelFilename};
415 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
416 pelFile.close();
417
418 std::string adItem = "RAWPEL=" + pelFilename.string();
419 std::vector<std::string> additionalData{adItem};
420 std::vector<std::string> associations;
421
422 manager.create("error message", 42, 0,
423 phosphor::logging::Entry::Level::Error, additionalData,
424 associations);
425
426 // getPELFromOBMCID
427 auto newData = manager.getPELFromOBMCID(42);
428 EXPECT_EQ(newData.size(), data.size());
429
430 // Read the PEL to get the ID for later
431 PEL pel{newData};
432 auto id = pel.id();
433
434 EXPECT_THROW(
435 manager.getPELFromOBMCID(id + 1),
436 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
437
438 // getPEL
439 auto unixfd = manager.getPEL(id);
440
441 // Get the size
442 struct stat s;
443 int r = fstat(unixfd, &s);
444 ASSERT_EQ(r, 0);
445 auto size = s.st_size;
446
447 // Open the FD and check the contents
448 FILE* fp = fdopen(unixfd, "r");
449 ASSERT_NE(fp, nullptr);
450
451 std::vector<uint8_t> fdData;
452 fdData.resize(size);
453 r = fread(fdData.data(), 1, size, fp);
454 EXPECT_EQ(r, size);
455
456 EXPECT_EQ(newData, fdData);
457
458 fclose(fp);
459
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600460 // Run the event loop to close the FD
461 sdeventplus::Event e{sdEvent};
462 e.run(std::chrono::milliseconds(1));
463
Matt Spinlera34ab722019-12-16 10:39:32 -0600464 EXPECT_THROW(
465 manager.getPEL(id + 1),
466 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
467
468 // hostAck
469 manager.hostAck(id);
470
471 EXPECT_THROW(
472 manager.hostAck(id + 1),
473 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
474
475 // hostReject
476 manager.hostReject(id, Manager::RejectionReason::BadPEL);
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600477
478 // Run the event loop to log the bad PEL event
479 e.run(std::chrono::milliseconds(1));
480
481 EXPECT_EQ(logger.errName, "org.open_power.Logging.Error.SentBadPELToHost");
482 EXPECT_EQ(id, std::stoi(logger.ad["BAD_ID"], nullptr, 16));
483
Matt Spinlera34ab722019-12-16 10:39:32 -0600484 manager.hostReject(id, Manager::RejectionReason::HostFull);
485
486 EXPECT_THROW(
487 manager.hostReject(id + 1, Manager::RejectionReason::BadPEL),
488 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
489
Ramesh Iyyarf4203c42021-06-24 06:09:23 -0500490 // GetPELIdFromBMCLogId
491 EXPECT_EQ(pel.id(), manager.getPELIdFromBMCLogId(pel.obmcLogID()));
492 EXPECT_THROW(
493 manager.getPELIdFromBMCLogId(pel.obmcLogID() + 1),
494 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
Ramesh Iyyar530efbf2021-06-24 06:22:22 -0500495
496 // GetBMCLogIdFromPELId
497 EXPECT_EQ(pel.obmcLogID(), manager.getBMCLogIdFromPELId(pel.id()));
498 EXPECT_THROW(
499 manager.getBMCLogIdFromPELId(pel.id() + 1),
500 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
Matt Spinlera34ab722019-12-16 10:39:32 -0600501}
Matt Spinler19e72902020-01-24 11:05:20 -0600502
503// An ESEL from the wild
504const std::string esel{
505 "00 00 df 00 00 00 00 20 00 04 12 01 6f aa 00 00 "
Matt Spinler0bf04b52023-04-28 10:30:26 -0500506 "50 48 00 30 01 00 33 00 20 23 05 11 10 20 20 00 00 00 00 07 5c d5 50 db "
Matt Spinler19e72902020-01-24 11:05:20 -0600507 "42 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 90 00 00 4e 90 00 00 4e "
508 "55 48 00 18 01 00 09 00 8a 03 40 00 00 00 00 00 ff ff 00 00 00 00 00 00 "
509 "50 53 00 50 01 01 00 00 02 00 00 09 33 2d 00 48 00 00 00 e0 00 00 10 00 "
510 "00 00 00 00 00 20 00 00 00 0c 00 02 00 00 00 fa 00 00 0c e4 00 00 00 12 "
511 "42 43 38 41 33 33 32 44 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 "
512 "20 20 20 20 20 20 20 20 55 44 00 1c 01 06 01 00 02 54 41 4b 00 00 00 06 "
513 "00 00 00 55 00 01 f9 20 00 00 00 00 55 44 00 24 01 06 01 00 01 54 41 4b "
514 "00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 23 01 00 02 00 05 00 00 "
515 "55 44 00 0c 01 0b 01 00 0f 01 00 00 55 44 00 10 01 04 01 00 0f 9f de 6a "
516 "00 01 00 00 55 44 00 7c 00 0c 01 00 00 13 0c 02 00 fa 0c e4 16 00 01 2c "
517 "0c 1c 16 00 00 fa 0a f0 14 00 00 fa 0b b8 14 00 00 be 09 60 12 00 01 2c "
518 "0d 7a 12 00 00 fa 0c 4e 10 00 00 fa 0c e4 10 00 00 be 0a 8c 16 00 01 2c "
519 "0c 1c 16 00 01 09 09 f6 16 00 00 fa 09 f6 14 00 00 fa 0b b8 14 00 00 fa "
520 "0a f0 14 00 00 be 08 ca 12 00 01 2c 0c e4 12 00 00 fa 0b 54 10 00 00 fa "
521 "0c 2d 10 00 00 be 08 ca 55 44 00 58 01 03 01 00 00 00 00 00 00 05 31 64 "
522 "00 00 00 00 00 05 0d d4 00 00 00 00 40 5f 06 e0 00 00 00 00 40 5d d2 00 "
523 "00 00 00 00 40 57 d3 d0 00 00 00 00 40 58 f6 a0 00 00 00 00 40 54 c9 34 "
524 "00 00 00 00 40 55 9a 10 00 00 00 00 40 4c 0a 80 00 00 00 00 00 00 27 14 "
525 "55 44 01 84 01 01 01 00 48 6f 73 74 62 6f 6f 74 20 42 75 69 6c 64 20 49 "
526 "44 3a 20 68 6f 73 74 62 6f 6f 74 2d 66 65 63 37 34 64 66 2d 70 30 61 38 "
527 "37 64 63 34 2f 68 62 69 63 6f 72 65 2e 62 69 6e 00 49 42 4d 2d 77 69 74 "
528 "68 65 72 73 70 6f 6f 6e 2d 4f 50 39 2d 76 32 2e 34 2d 39 2e 32 33 34 0a "
529 "09 6f 70 2d 62 75 69 6c 64 2d 38 32 66 34 63 66 30 0a 09 62 75 69 6c 64 "
530 "72 6f 6f 74 2d 32 30 31 39 2e 30 35 2e 32 2d 31 30 2d 67 38 39 35 39 31 "
531 "31 34 0a 09 73 6b 69 62 6f 6f 74 2d 76 36 2e 35 2d 31 38 2d 67 34 37 30 "
532 "66 66 62 35 66 32 39 64 37 0a 09 68 6f 73 74 62 6f 6f 74 2d 66 65 63 37 "
533 "34 64 66 2d 70 30 61 38 37 64 63 34 0a 09 6f 63 63 2d 65 34 35 39 37 61 "
534 "62 0a 09 6c 69 6e 75 78 2d 35 2e 32 2e 31 37 2d 6f 70 65 6e 70 6f 77 65 "
535 "72 31 2d 70 64 64 63 63 30 33 33 0a 09 70 65 74 69 74 62 6f 6f 74 2d 76 "
536 "31 2e 31 30 2e 34 0a 09 6d 61 63 68 69 6e 65 2d 78 6d 6c 2d 63 36 32 32 "
537 "63 62 35 2d 70 37 65 63 61 62 33 64 0a 09 68 6f 73 74 62 6f 6f 74 2d 62 "
538 "69 6e 61 72 69 65 73 2d 36 36 65 39 61 36 30 0a 09 63 61 70 70 2d 75 63 "
539 "6f 64 65 2d 70 39 2d 64 64 32 2d 76 34 0a 09 73 62 65 2d 36 30 33 33 30 "
540 "65 30 0a 09 68 63 6f 64 65 2d 68 77 30 39 32 31 31 39 61 2e 6f 70 6d 73 "
541 "74 0a 00 00 55 44 00 70 01 04 01 00 0f 9f de 6a 00 05 00 00 07 5f 1d f4 "
542 "30 32 43 59 34 37 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
543 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
544 "0b ac 54 02 59 41 31 39 33 34 36 39 37 30 35 38 00 00 00 00 00 00 05 22 "
545 "a1 58 01 8a 00 58 40 20 17 18 4d 2c 00 00 00 fc 01 a1 00 00 55 44 00 14 "
546 "01 08 01 00 00 00 00 01 00 00 00 5a 00 00 00 05 55 44 03 fc 01 15 31 00 "
547 "01 28 00 42 46 41 50 49 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 f4 "
548 "00 00 00 00 00 00 03 f4 00 00 00 0b 00 00 00 00 00 00 00 3d 2c 9b c2 84 "
549 "00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 09 "
550 "00 00 00 00 00 11 bd 20 00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 "
551 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 00 00 00 00 00 00 01 2c "
552 "00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0c 1c 00 00 00 64 00 00 00 3d "
553 "2c 9b d1 11 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 "
554 "00 00 00 0a 00 00 00 00 00 13 b5 a0 00 00 00 00 00 01 f8 80 00 00 00 00 "
555 "00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 "
556 "00 00 00 be 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0a 8c 00 00 00 64 "
557 "00 00 00 3d 2c 9b df 98 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 "
558 "00 00 00 00 00 00 00 0b 00 00 00 00 00 15 ae 20 00 00 00 00 00 01 f8 80 "
559 "00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 "
560 "00 00 00 00 00 00 00 fa 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0c e4 "
561 "00 00 00 64 00 00 00 3d 2c 9b ea b7 00 00 01 e4 00 48 43 4f fb ed 70 b1 "
562 "00 00 02 01 00 00 00 00 00 00 00 0c 00 00 00 00 00 17 a6 a0 00 00 00 00 "
563 "00 01 f8 80 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 "
564 "00 00 00 12 00 00 00 00 00 00 00 fa 00 00 00 00 00 00 07 d0 00 00 00 00 "
565 "00 00 0c 4e 00 00 00 64 00 00 00 3d 2c 9b f6 27 00 00 01 e4 00 48 43 4f "
566 "fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 0d 00 00 00 00 00 19 9f 20 "
567 "00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 "
568 "00 00 00 00 00 00 00 12 00 00 00 00 00 00 01 2c 00 00 00 00 00 00 07 d0 "
569 "00 00 00 00 00 00 0d 7a 00 00 00 64 00 00 00 3d 2c 9c 05 75 00 00 01 e4 "
570 "00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 0e 00 00 00 00 "
571 "00 1b 97 a0 00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 00 00 00 00 "
572 "00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00 be 00 00 00 00 "
573 "00 00 07 d0 00 00 00 00 00 00 09 60 00 00 00 64 00 00 00 3d 2c 9c 11 29 "
574 "00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 0f "
575 "00 00 00 00 00 1d 90 20 00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 "
576 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00 fa "
577 "00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0b b8 00 00 00 64 00 00 00 3d "
578 "2c 9c 1c 45 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 "
579 "00 00 00 10 00 00 00 00 00 1f 88 a0 00 00 00 00 00 01 f8 80 00 00 00 00 "
580 "00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 00 00 00 00 "
581 "00 00 00 fa 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0a f0 00 00 00 64 "
582 "00 00 00 3d 2c 9c 2b 14 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 "
583 "00 00 00 00 00 00 00 11 00 00 00 00 00 21 81 20 00 00 00 00 00 01 f8 80 "
584 "00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 "
585 "00 00 00 00 00 00 01 2c 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0c 1c "
586 "00 00 00 64 00 00 00 3d 2d 6d 8f 9e 00 00 01 e4 00 00 43 4f 52 d7 9c 36 "
587 "00 00 04 73 00 00 00 1c 00 00 00 3d 2d 6d 99 ac 00 00 01 e4 00 10 43 4f "
588 "3f f2 02 3d 00 00 05 58 00 00 00 00 02 00 00 01 00 00 00 00 00 00 00 40 "
589 "00 00 00 2c 55 44 00 30 01 15 31 00 01 28 00 42 46 41 50 49 5f 44 42 47 "
590 "00 00 00 00 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00 28 00 00 00 00 "
591 "00 00 00 00 55 44 01 74 01 15 31 00 01 28 00 42 46 41 50 49 5f 49 00 00 "
592 "00 00 00 00 00 00 00 00 00 00 01 6c 00 00 00 00 00 00 01 6c 00 00 00 0b "
593 "00 00 00 00 00 00 00 3c 0d 52 18 5e 00 00 01 e4 00 08 43 4f 46 79 94 13 "
594 "00 00 0a 5b 00 00 00 00 00 00 2c 00 00 00 00 24 00 00 00 3c 0d 6b 26 6c "
595 "00 00 01 e4 00 00 43 4f 4e 9b 18 74 00 00 01 03 00 00 00 1c 00 00 00 3c "
596 "12 b9 2d 13 00 00 01 e4 00 00 43 4f ea 31 ed d4 00 00 05 c4 00 00 00 1c "
597 "00 00 00 3c 13 02 73 53 00 00 01 e4 00 00 43 4f ea 31 ed d4 00 00 05 c4 "
598 "00 00 00 1c 00 00 00 3c 13 04 7c 94 00 00 01 e4 00 00 43 4f ea 31 ed d4 "
599 "00 00 05 c4 00 00 00 1c 00 00 00 3c 13 06 ad e1 00 00 01 e4 00 00 43 4f "
600 "ea 31 ed d4 00 00 05 c4 00 00 00 1c 00 00 00 3c 13 07 3f 77 00 00 01 e4 "
601 "00 00 43 4f 5e 4a 55 32 00 00 10 f2 00 00 00 1c 00 00 00 3c 13 07 4e e4 "
602 "00 00 01 e4 00 00 43 4f 5e 4a 55 32 00 00 0d 68 00 00 00 1c 00 00 00 3c "
603 "13 36 79 18 00 00 01 e4 00 00 43 4f ea 31 ed d4 00 00 05 c4 00 00 00 1c "
604 "00 00 00 3d 2c 9c 36 70 00 00 01 e4 00 00 43 4f 23 45 90 97 00 00 02 47 "
605 "00 00 00 1c 00 00 00 3d 2d 6d a3 ed 00 00 01 e4 00 08 43 4f 74 3a 5b 1a "
606 "00 00 04 cc 00 00 00 00 02 00 00 01 00 00 00 24 55 44 00 30 01 15 31 00 "
607 "01 28 00 42 53 43 41 4e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 28 "
608 "00 00 00 00 00 00 00 28 00 00 00 00 00 00 00 00"};
609
610TEST_F(ManagerTest, TestESELToRawData)
611{
612 auto data = Manager::eselToRawData(esel);
613
614 EXPECT_EQ(data.size(), 2464);
615
616 PEL pel{data};
617 EXPECT_TRUE(pel.valid());
618}
619
620TEST_F(ManagerTest, TestCreateWithESEL)
621{
622 std::unique_ptr<DataInterfaceBase> dataIface =
Matt Spinlere6b48f12020-04-02 09:51:39 -0500623 std::make_unique<MockDataInterface>();
Matt Spinler19e72902020-01-24 11:05:20 -0600624
Matt Spinlerd96fa602022-12-15 11:11:26 -0600625 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
626
Matt Spinler19e72902020-01-24 11:05:20 -0600627 openpower::pels::Manager manager{
628 logManager, std::move(dataIface),
629 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
Matt Spinlerd96fa602022-12-15 11:11:26 -0600630 std::placeholders::_2, std::placeholders::_3),
631 std::move(journal)};
Matt Spinler19e72902020-01-24 11:05:20 -0600632
633 {
634 std::string adItem = "ESEL=" + esel;
635 std::vector<std::string> additionalData{adItem};
636 std::vector<std::string> associations;
637
638 manager.create("error message", 37, 0,
639 phosphor::logging::Entry::Level::Error, additionalData,
640 associations);
641
642 auto data = manager.getPELFromOBMCID(37);
643 PEL pel{data};
644 EXPECT_TRUE(pel.valid());
645 }
646
647 // Now an invalid one
648 {
649 std::string adItem = "ESEL=" + esel;
650
651 // Crop it
652 adItem.resize(adItem.size() - 300);
653
654 std::vector<std::string> additionalData{adItem};
655 std::vector<std::string> associations;
656
657 manager.create("error message", 38, 0,
658 phosphor::logging::Entry::Level::Error, additionalData,
659 associations);
660
661 EXPECT_THROW(
662 manager.getPELFromOBMCID(38),
663 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
664
665 // Run the event loop to log the bad PEL event
666 sdeventplus::Event e{sdEvent};
667 e.run(std::chrono::milliseconds(1));
668
669 EXPECT_EQ(logger.errName, "org.open_power.Logging.Error.BadHostPEL");
670 EXPECT_EQ(logger.errLevel, phosphor::logging::Entry::Level::Error);
671 }
672}
Matt Spinler7e727a32020-07-07 15:00:17 -0500673
674// Test that PELs will be pruned when necessary
675TEST_F(ManagerTest, TestPruning)
676{
677 sdeventplus::Event e{sdEvent};
678
679 std::unique_ptr<DataInterfaceBase> dataIface =
680 std::make_unique<MockDataInterface>();
681
Matt Spinlerd96fa602022-12-15 11:11:26 -0600682 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
683
Matt Spinler7e727a32020-07-07 15:00:17 -0500684 openpower::pels::Manager manager{
685 logManager, std::move(dataIface),
686 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
Matt Spinlerd96fa602022-12-15 11:11:26 -0600687 std::placeholders::_2, std::placeholders::_3),
688 std::move(journal)};
Matt Spinler7e727a32020-07-07 15:00:17 -0500689
690 // Create 25 1000B (4096B on disk each, which is what is used for pruning)
691 // BMC non-informational PELs in the 100KB repository. After the 24th one,
692 // the repo will be 96% full and a prune should be triggered to remove all
693 // but 7 to get under 30% full. Then when the 25th is added there will be
694 // 8 left.
695
696 auto dir = makeTempDir();
697 for (int i = 1; i <= 25; i++)
698 {
699 auto data = pelFactory(42, 'O', 0x40, 0x8800, 1000);
700
701 fs::path pelFilename = dir / "rawpel";
702 std::ofstream pelFile{pelFilename};
703 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
704 pelFile.close();
705
706 std::string adItem = "RAWPEL=" + pelFilename.string();
707 std::vector<std::string> additionalData{adItem};
708 std::vector<std::string> associations;
709
710 manager.create("error message", 42, 0,
711 phosphor::logging::Entry::Level::Error, additionalData,
712 associations);
713
714 // Simulate the code getting back to the event loop
715 // after each create.
716 e.run(std::chrono::milliseconds(1));
717
718 if (i < 24)
719 {
720 EXPECT_EQ(countPELsInRepo(), i);
721 }
722 else if (i == 24)
723 {
724 // Prune occured
725 EXPECT_EQ(countPELsInRepo(), 7);
726 }
727 else // i == 25
728 {
729 EXPECT_EQ(countPELsInRepo(), 8);
730 }
731 }
732
733 try
734 {
735 // Make sure the 8 newest ones are still found.
736 for (uint32_t i = 0; i < 8; i++)
737 {
738 manager.getPEL(0x50000012 + i);
739 }
740 }
Patrick Williams66491c62021-10-06 12:23:37 -0500741 catch (
742 const sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument&
Matt Spinlerbe952d22022-07-01 11:30:11 -0500743 ex)
Matt Spinler7e727a32020-07-07 15:00:17 -0500744 {
745 ADD_FAILURE() << "PELs should have all been found";
746 }
Matt Spinler7e727a32020-07-07 15:00:17 -0500747}
Matt Spinlerff9cec22020-07-15 13:06:35 -0500748
749// Test that manually deleting a PEL file will be recognized by the code.
750TEST_F(ManagerTest, TestPELManualDelete)
751{
752 sdeventplus::Event e{sdEvent};
753
754 std::unique_ptr<DataInterfaceBase> dataIface =
755 std::make_unique<MockDataInterface>();
756
Matt Spinlerd96fa602022-12-15 11:11:26 -0600757 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
758
Matt Spinlerff9cec22020-07-15 13:06:35 -0500759 openpower::pels::Manager manager{
760 logManager, std::move(dataIface),
761 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
Matt Spinlerd96fa602022-12-15 11:11:26 -0600762 std::placeholders::_2, std::placeholders::_3),
763 std::move(journal)};
Matt Spinlerff9cec22020-07-15 13:06:35 -0500764
765 auto data = pelDataFactory(TestPELType::pelSimple);
766 auto dir = makeTempDir();
767 fs::path pelFilename = dir / "rawpel";
768
769 std::string adItem = "RAWPEL=" + pelFilename.string();
770 std::vector<std::string> additionalData{adItem};
771 std::vector<std::string> associations;
772
773 // Add 20 PELs, they will get incrementing IDs like
774 // 0x50000001, 0x50000002, etc.
775 for (int i = 1; i <= 20; i++)
776 {
777 std::ofstream pelFile{pelFilename};
778 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
779 pelFile.close();
780
781 manager.create("error message", 42, 0,
782 phosphor::logging::Entry::Level::Error, additionalData,
783 associations);
784
785 // Sanity check this ID is really there so we can test
786 // it was deleted later. This will throw an exception if
787 // not present.
788 manager.getPEL(0x50000000 + i);
789
790 // Run an event loop pass where the internal FD is deleted
791 // after the getPEL function call.
792 e.run(std::chrono::milliseconds(1));
793 }
794
795 EXPECT_EQ(countPELsInRepo(), 20);
796
797 deletePELFile(0x50000001);
798
799 // Run a single event loop pass so the inotify event can run
800 e.run(std::chrono::milliseconds(1));
801
802 EXPECT_EQ(countPELsInRepo(), 19);
803
804 EXPECT_THROW(
805 manager.getPEL(0x50000001),
806 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
807
808 // Delete a few more, they should all get handled in the same
809 // event loop pass
810 std::vector<uint32_t> toDelete{0x50000002, 0x50000003, 0x50000004,
811 0x50000005, 0x50000006};
812 std::for_each(toDelete.begin(), toDelete.end(),
813 [](auto i) { deletePELFile(i); });
814
815 e.run(std::chrono::milliseconds(1));
816
817 EXPECT_EQ(countPELsInRepo(), 14);
818
819 std::for_each(toDelete.begin(), toDelete.end(), [&manager](const auto i) {
820 EXPECT_THROW(
821 manager.getPEL(i),
822 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
823 });
Matt Spinlerff9cec22020-07-15 13:06:35 -0500824}
825
826// Test that deleting all PELs at once is handled OK.
827TEST_F(ManagerTest, TestPELManualDeleteAll)
828{
829 sdeventplus::Event e{sdEvent};
830
831 std::unique_ptr<DataInterfaceBase> dataIface =
832 std::make_unique<MockDataInterface>();
833
Matt Spinlerd96fa602022-12-15 11:11:26 -0600834 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
835
Matt Spinlerff9cec22020-07-15 13:06:35 -0500836 openpower::pels::Manager manager{
837 logManager, std::move(dataIface),
838 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
Matt Spinlerd96fa602022-12-15 11:11:26 -0600839 std::placeholders::_2, std::placeholders::_3),
840 std::move(journal)};
Matt Spinlerff9cec22020-07-15 13:06:35 -0500841
842 auto data = pelDataFactory(TestPELType::pelSimple);
843 auto dir = makeTempDir();
844 fs::path pelFilename = dir / "rawpel";
845
846 std::string adItem = "RAWPEL=" + pelFilename.string();
847 std::vector<std::string> additionalData{adItem};
848 std::vector<std::string> associations;
849
850 // Add 200 PELs, they will get incrementing IDs like
851 // 0x50000001, 0x50000002, etc.
852 for (int i = 1; i <= 200; i++)
853 {
854 std::ofstream pelFile{pelFilename};
855 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
856 pelFile.close();
857
858 manager.create("error message", 42, 0,
859 phosphor::logging::Entry::Level::Error, additionalData,
860 associations);
861
862 // Sanity check this ID is really there so we can test
863 // it was deleted later. This will throw an exception if
864 // not present.
865 manager.getPEL(0x50000000 + i);
866
867 // Run an event loop pass where the internal FD is deleted
868 // after the getPEL function call.
869 e.run(std::chrono::milliseconds(1));
870 }
871
872 // Delete them all at once
873 auto logPath = getPELRepoPath() / "logs";
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500874 std::string cmd = "rm " + logPath.string() + "/*_*";
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500875
876 {
877 auto rc = system(cmd.c_str());
878 EXPECT_EQ(rc, 0);
879 }
Matt Spinlerff9cec22020-07-15 13:06:35 -0500880
881 EXPECT_EQ(countPELsInRepo(), 0);
882
883 // It will take 5 event loop passes to process them all
884 for (int i = 0; i < 5; i++)
885 {
886 e.run(std::chrono::milliseconds(1));
887 }
888
889 for (int i = 1; i <= 200; i++)
890 {
891 EXPECT_THROW(
892 manager.getPEL(0x50000000 + i),
893 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
894 }
Matt Spinlerff9cec22020-07-15 13:06:35 -0500895}
Matt Spinler3dd17e92020-08-05 15:04:27 -0500896
897// Test that fault LEDs are turned on when PELs are created
898TEST_F(ManagerTest, TestServiceIndicators)
899{
900 std::unique_ptr<DataInterfaceBase> dataIface =
901 std::make_unique<MockDataInterface>();
902
903 MockDataInterface* mockIface =
904 reinterpret_cast<MockDataInterface*>(dataIface.get());
905
Sumit Kumar9d43a722021-08-24 09:46:19 -0500906 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
907 "system/entry"};
908 EXPECT_CALL(*mockIface, checkDumpStatus(dumpType))
909 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
910
Matt Spinlerd96fa602022-12-15 11:11:26 -0600911 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
912
Matt Spinler3dd17e92020-08-05 15:04:27 -0500913 openpower::pels::Manager manager{
914 logManager, std::move(dataIface),
915 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
Matt Spinlerd96fa602022-12-15 11:11:26 -0600916 std::placeholders::_2, std::placeholders::_3),
917 std::move(journal)};
Matt Spinler3dd17e92020-08-05 15:04:27 -0500918
919 // Add a PEL with a callout as if hostboot added it
920 {
921 EXPECT_CALL(*mockIface, getInventoryFromLocCode("U42", 0, true))
Matt Spinlerbad056b2023-01-25 14:16:57 -0600922 .WillOnce(
923 Return(std::vector<std::string>{"/system/chassis/processor"}));
Matt Spinler3dd17e92020-08-05 15:04:27 -0500924
Matt Spinler993168d2021-04-07 16:05:03 -0500925 EXPECT_CALL(*mockIface,
926 setFunctional("/system/chassis/processor", false))
Matt Spinler3dd17e92020-08-05 15:04:27 -0500927 .Times(1);
928
929 // This hostboot PEL has a single hardware callout in it.
930 auto data = pelFactory(1, 'B', 0x20, 0xA400, 500);
931
932 fs::path pelFilename = makeTempDir() / "rawpel";
933 std::ofstream pelFile{pelFilename};
934 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
935 pelFile.close();
936
937 std::string adItem = "RAWPEL=" + pelFilename.string();
938 std::vector<std::string> additionalData{adItem};
939 std::vector<std::string> associations;
940
941 manager.create("error message", 42, 0,
942 phosphor::logging::Entry::Level::Error, additionalData,
943 associations);
Matt Spinler3dd17e92020-08-05 15:04:27 -0500944 }
945
946 // Add a BMC PEL with a callout that uses the message registry
947 {
948 std::vector<std::string> names{"systemA"};
949 EXPECT_CALL(*mockIface, getSystemNames)
950 .Times(1)
Matt Spinler1ab66962020-10-29 13:21:44 -0500951 .WillOnce(Return(names));
Matt Spinler3dd17e92020-08-05 15:04:27 -0500952
953 EXPECT_CALL(*mockIface, expandLocationCode("P42-C23", 0))
954 .WillOnce(Return("U42-P42-C23"));
955
956 // First call to this is when building the Callout section
957 EXPECT_CALL(*mockIface, getInventoryFromLocCode("P42-C23", 0, false))
Matt Spinlerbad056b2023-01-25 14:16:57 -0600958 .WillOnce(
959 Return(std::vector<std::string>{"/system/chassis/processor"}));
Matt Spinler3dd17e92020-08-05 15:04:27 -0500960
961 // Second call to this is finding the associated LED group
962 EXPECT_CALL(*mockIface, getInventoryFromLocCode("U42-P42-C23", 0, true))
Matt Spinlerbad056b2023-01-25 14:16:57 -0600963 .WillOnce(
964 Return(std::vector<std::string>{"/system/chassis/processor"}));
Matt Spinler3dd17e92020-08-05 15:04:27 -0500965
Matt Spinler993168d2021-04-07 16:05:03 -0500966 EXPECT_CALL(*mockIface,
967 setFunctional("/system/chassis/processor", false))
Matt Spinler3dd17e92020-08-05 15:04:27 -0500968 .Times(1);
969
970 const auto registry = R"(
971 {
972 "PELs":
973 [
974 {
975 "Name": "xyz.openbmc_project.Error.Test",
976 "Subsystem": "power_supply",
977 "ActionFlags": ["service_action", "report"],
978 "SRC":
979 {
980 "ReasonCode": "0x2030"
981 },
982 "Callouts": [
983 {
984 "CalloutList": [
985 {"Priority": "high", "LocCode": "P42-C23"}
986 ]
987 }
988 ],
989 "Documentation":
990 {
991 "Description": "Test Error",
992 "Message": "Test Error"
993 }
994 }
995 ]
996 })";
997
998 auto path = getPELReadOnlyDataPath();
999 fs::create_directories(path);
1000 path /= "message_registry.json";
1001
1002 std::ofstream registryFile{path};
1003 registryFile << registry;
1004 registryFile.close();
1005
1006 std::vector<std::string> additionalData;
1007 std::vector<std::string> associations;
1008
1009 manager.create("xyz.openbmc_project.Error.Test", 42, 0,
1010 phosphor::logging::Entry::Level::Error, additionalData,
1011 associations);
1012 }
1013}
Sumit Kumar2ccdcef2021-07-31 10:04:58 -05001014
1015// Test for duplicate PELs moved to archive folder
1016TEST_F(ManagerTest, TestDuplicatePEL)
1017{
1018 sdeventplus::Event e{sdEvent};
1019 size_t count = 0;
1020
1021 std::unique_ptr<DataInterfaceBase> dataIface =
1022 std::make_unique<MockDataInterface>();
1023
Matt Spinlerd96fa602022-12-15 11:11:26 -06001024 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
1025
Sumit Kumar2ccdcef2021-07-31 10:04:58 -05001026 openpower::pels::Manager manager{
1027 logManager, std::move(dataIface),
1028 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
Matt Spinlerd96fa602022-12-15 11:11:26 -06001029 std::placeholders::_2, std::placeholders::_3),
1030 std::move(journal)};
Sumit Kumar2ccdcef2021-07-31 10:04:58 -05001031
1032 for (int i = 0; i < 2; i++)
1033 {
1034 // This hostboot PEL has a single hardware callout in it.
1035 auto data = pelFactory(1, 'B', 0x20, 0xA400, 500);
1036
1037 fs::path pelFilename = makeTempDir() / "rawpel";
1038 std::ofstream pelFile{pelFilename};
1039 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
1040 pelFile.close();
1041
1042 std::string adItem = "RAWPEL=" + pelFilename.string();
1043 std::vector<std::string> additionalData{adItem};
1044 std::vector<std::string> associations;
1045
1046 manager.create("error message", 42, 0,
1047 phosphor::logging::Entry::Level::Error, additionalData,
1048 associations);
1049
1050 e.run(std::chrono::milliseconds(1));
1051 }
1052
1053 for (auto& f :
1054 fs::directory_iterator(getPELRepoPath() / "logs" / "archive"))
1055 {
1056 if (fs::is_regular_file(f.path()))
1057 {
1058 count++;
1059 }
1060 }
1061
1062 // Get count of PELs in the repository & in archive directtory
1063 EXPECT_EQ(countPELsInRepo(), 1);
1064 EXPECT_EQ(count, 1);
1065}
Sumit Kumar3e274432021-09-14 06:37:56 -05001066
1067// Test termination bit set for pel with critical system termination severity
1068TEST_F(ManagerTest, TestTerminateBitWithPELSevCriticalSysTerminate)
1069{
1070 const auto registry = R"(
1071{
1072 "PELs":
1073 [
1074 {
1075 "Name": "xyz.openbmc_project.Error.Test",
1076 "Subsystem": "power_supply",
1077 "Severity": "critical_system_term",
1078 "ActionFlags": ["service_action", "report"],
1079 "SRC":
1080 {
1081 "ReasonCode": "0x2030"
1082 },
1083 "Documentation":
1084 {
1085 "Description": "A PGOOD Fault",
1086 "Message": "PS had a PGOOD Fault"
1087 }
1088 }
1089 ]
1090}
1091)";
1092
1093 auto path = getPELReadOnlyDataPath();
1094 fs::create_directories(path);
1095 path /= "message_registry.json";
1096
1097 std::ofstream registryFile{path};
1098 registryFile << registry;
1099 registryFile.close();
1100
1101 std::unique_ptr<DataInterfaceBase> dataIface =
1102 std::make_unique<MockDataInterface>();
1103
1104 MockDataInterface* mockIface =
1105 reinterpret_cast<MockDataInterface*>(dataIface.get());
1106
1107 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1108 "system/entry"};
1109 EXPECT_CALL(*mockIface, checkDumpStatus(dumpType))
1110 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
1111
Matt Spinlerd96fa602022-12-15 11:11:26 -06001112 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
1113
Sumit Kumar3e274432021-09-14 06:37:56 -05001114 openpower::pels::Manager manager{
1115 logManager, std::move(dataIface),
1116 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
Matt Spinlerd96fa602022-12-15 11:11:26 -06001117 std::placeholders::_2, std::placeholders::_3),
1118 std::move(journal)};
Sumit Kumar3e274432021-09-14 06:37:56 -05001119
1120 std::vector<std::string> additionalData{"FOO=BAR"};
1121 std::vector<std::string> associations;
1122
1123 // Create the event log to create the PEL from.
1124 manager.create("xyz.openbmc_project.Error.Test", 33, 0,
1125 phosphor::logging::Entry::Level::Error, additionalData,
1126 associations);
1127
1128 // Ensure a PEL was created in the repository
1129 auto pelData = findAnyPELInRepo();
1130 ASSERT_TRUE(pelData);
1131
1132 auto getPELData = readPELFile(*pelData);
1133 PEL pel(*getPELData);
1134
1135 // Spot check it. Other testcases cover the details.
1136 EXPECT_TRUE(pel.valid());
1137
1138 // Check for terminate bit set
1139 auto& hexwords = pel.primarySRC().value()->hexwordData();
1140 EXPECT_EQ(hexwords[3] & 0x20000000, 0x20000000);
1141}
Matt Spinler0003af12022-06-08 10:46:17 -05001142
1143TEST_F(ManagerTest, TestSanitizeFieldforDBus)
1144{
1145 std::string base{"(test0!}\n\t ~"};
1146 auto string = base;
1147 string += char{' ' - 1};
1148 string += char{'~' + 1};
1149 string += char{0};
1150 string += char{static_cast<char>(0xFF)};
1151
1152 // convert the last four chars to spaces
1153 EXPECT_EQ(Manager::sanitizeFieldForDBus(string), base + " ");
1154}