/** * Copyright © 2019 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "extensions/openpower-pels/manager.hpp" #include "log_manager.hpp" #include "pel_utils.hpp" #include #include #include #include using namespace openpower::pels; namespace fs = std::filesystem; class TestLogger { public: void log(const std::string& name, phosphor::logging::Entry::Level level, const EventLogger::ADMap& additionalData) { errName = name; errLevel = level; ad = additionalData; } std::string errName; phosphor::logging::Entry::Level errLevel; EventLogger::ADMap ad; }; class ManagerTest : public CleanPELFiles { public: ManagerTest() : logManager(bus, "logging_path") { sd_event_default(&sdEvent); bus.attach_event(sdEvent, SD_EVENT_PRIORITY_NORMAL); } ~ManagerTest() { sd_event_unref(sdEvent); } sdbusplus::bus::bus bus = sdbusplus::bus::new_default(); phosphor::logging::internal::Manager logManager; sd_event* sdEvent; TestLogger logger; }; fs::path makeTempDir() { char path[] = "/tmp/tempnameXXXXXX"; std::filesystem::path dir = mkdtemp(path); return dir; } std::optional findAnyPELInRepo() { // PELs are named _ std::regex expr{"\\d+_\\d+"}; for (auto& f : fs::directory_iterator(getPELRepoPath() / "logs")) { if (std::regex_search(f.path().string(), expr)) { return f.path(); } } return std::nullopt; } // Test that using the RAWPEL= with the Manager::create() call gets // a PEL saved in the repository. TEST_F(ManagerTest, TestCreateWithPEL) { std::unique_ptr dataIface = std::make_unique(bus); openpower::pels::Manager manager{ logManager, std::move(dataIface), std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)}; // Create a PEL, write it to a file, and pass that filename into // the create function. auto data = pelDataFactory(TestPELType::pelSimple); fs::path pelFilename = makeTempDir() / "rawpel"; std::ofstream pelFile{pelFilename}; pelFile.write(reinterpret_cast(data.data()), data.size()); pelFile.close(); std::string adItem = "RAWPEL=" + pelFilename.string(); std::vector additionalData{adItem}; std::vector associations; manager.create("error message", 42, 0, phosphor::logging::Entry::Level::Error, additionalData, associations); // Find the file in the PEL repository directory auto pelPathInRepo = findAnyPELInRepo(); EXPECT_TRUE(pelPathInRepo); // Now remove it based on its OpenBMC event log ID manager.erase(42); pelPathInRepo = findAnyPELInRepo(); EXPECT_FALSE(pelPathInRepo); fs::remove_all(pelFilename.parent_path()); } TEST_F(ManagerTest, TestCreateWithInvalidPEL) { std::unique_ptr dataIface = std::make_unique(bus); openpower::pels::Manager manager{ logManager, std::move(dataIface), std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)}; // Create a PEL, write it to a file, and pass that filename into // the create function. auto data = pelDataFactory(TestPELType::pelSimple); // Truncate it to make it invalid. data.resize(200); fs::path pelFilename = makeTempDir() / "rawpel"; std::ofstream pelFile{pelFilename}; pelFile.write(reinterpret_cast(data.data()), data.size()); pelFile.close(); std::string adItem = "RAWPEL=" + pelFilename.string(); std::vector additionalData{adItem}; std::vector associations; manager.create("error message", 42, 0, phosphor::logging::Entry::Level::Error, additionalData, associations); // Run the event loop to log the bad PEL event sdeventplus::Event e{sdEvent}; e.run(std::chrono::milliseconds(1)); PEL invalidPEL{data}; EXPECT_EQ(logger.errName, "org.open_power.Logging.Error.BadHostPEL"); EXPECT_EQ(logger.errLevel, phosphor::logging::Entry::Level::Error); EXPECT_EQ(std::stoi(logger.ad["PLID"], nullptr, 16), invalidPEL.plid()); EXPECT_EQ(logger.ad["OBMC_LOG_ID"], "42"); EXPECT_EQ(logger.ad["SRC"], (*invalidPEL.primarySRC())->asciiString()); EXPECT_EQ(logger.ad["PEL_SIZE"], std::to_string(data.size())); fs::remove_all(pelFilename.parent_path()); } // Test that the message registry can be used to build a PEL. TEST_F(ManagerTest, TestCreateWithMessageRegistry) { const auto registry = R"( { "PELs": [ { "Name": "xyz.openbmc_project.Error.Test", "Subsystem": "power_supply", "ActionFlags": ["service_action", "report"], "SRC": { "ReasonCode": "0x2030" }, "Documentation": { "Description": "A PGOOD Fault", "Message": "PS had a PGOOD Fault" } } ] } )"; auto path = getMessageRegistryPath(); fs::create_directories(path); path /= "message_registry.json"; std::ofstream registryFile{path}; registryFile << registry; registryFile.close(); std::unique_ptr dataIface = std::make_unique(logManager.getBus()); openpower::pels::Manager manager{ logManager, std::move(dataIface), std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)}; std::vector additionalData; std::vector associations; // Create the event log to create the PEL from. manager.create("xyz.openbmc_project.Error.Test", 33, 0, phosphor::logging::Entry::Level::Error, additionalData, associations); // Ensure a PEL was created in the repository auto pelFile = findAnyPELInRepo(); ASSERT_TRUE(pelFile); auto data = readPELFile(*pelFile); PEL pel(*data); // Spot check it. Other testcases cover the details. EXPECT_TRUE(pel.valid()); EXPECT_EQ(pel.obmcLogID(), 33); EXPECT_EQ(pel.primarySRC().value()->asciiString(), "BD612030 "); // Remove it manager.erase(33); pelFile = findAnyPELInRepo(); EXPECT_FALSE(pelFile); // Create an event log that can't be found in the registry. manager.create("xyz.openbmc_project.Error.Foo", 33, 0, phosphor::logging::Entry::Level::Error, additionalData, associations); // Currently, no PEL should be created. Eventually, a 'missing registry // entry' PEL will be there. pelFile = findAnyPELInRepo(); EXPECT_FALSE(pelFile); } TEST_F(ManagerTest, TestDBusMethods) { std::unique_ptr dataIface = std::make_unique(bus); Manager manager{logManager, std::move(dataIface), std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)}; // Create a PEL, write it to a file, and pass that filename into // the create function so there's one in the repo. auto data = pelDataFactory(TestPELType::pelSimple); fs::path pelFilename = makeTempDir() / "rawpel"; std::ofstream pelFile{pelFilename}; pelFile.write(reinterpret_cast(data.data()), data.size()); pelFile.close(); std::string adItem = "RAWPEL=" + pelFilename.string(); std::vector additionalData{adItem}; std::vector associations; manager.create("error message", 42, 0, phosphor::logging::Entry::Level::Error, additionalData, associations); // getPELFromOBMCID auto newData = manager.getPELFromOBMCID(42); EXPECT_EQ(newData.size(), data.size()); // Read the PEL to get the ID for later PEL pel{newData}; auto id = pel.id(); EXPECT_THROW( manager.getPELFromOBMCID(id + 1), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); // getPEL auto unixfd = manager.getPEL(id); // Get the size struct stat s; int r = fstat(unixfd, &s); ASSERT_EQ(r, 0); auto size = s.st_size; // Open the FD and check the contents FILE* fp = fdopen(unixfd, "r"); ASSERT_NE(fp, nullptr); std::vector fdData; fdData.resize(size); r = fread(fdData.data(), 1, size, fp); EXPECT_EQ(r, size); EXPECT_EQ(newData, fdData); fclose(fp); // Run the event loop to close the FD sdeventplus::Event e{sdEvent}; e.run(std::chrono::milliseconds(1)); EXPECT_THROW( manager.getPEL(id + 1), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); // hostAck manager.hostAck(id); EXPECT_THROW( manager.hostAck(id + 1), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); // hostReject manager.hostReject(id, Manager::RejectionReason::BadPEL); // Run the event loop to log the bad PEL event e.run(std::chrono::milliseconds(1)); EXPECT_EQ(logger.errName, "org.open_power.Logging.Error.SentBadPELToHost"); EXPECT_EQ(id, std::stoi(logger.ad["BAD_ID"], nullptr, 16)); manager.hostReject(id, Manager::RejectionReason::HostFull); EXPECT_THROW( manager.hostReject(id + 1, Manager::RejectionReason::BadPEL), sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); fs::remove_all(pelFilename.parent_path()); }