/** * Copyright © 2018 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 "policy_find.hpp" #include "policy_table.hpp" #include #include #include using namespace ibm::logging; namespace fs = std::experimental::filesystem; static constexpr auto HOST_EVENT = "org.open_power.Host.Error.Event"; // ESEL contents all of the way up to right before the severity // byte in the UH section static const std::string eSELBase = "ESEL=" "00 00 df 00 00 00 00 20 00 04 07 5a 04 aa 00 00 50 48 00 30 01 00 e5 00 " "00 00 f6 ca c9 da 5b b7 00 00 f6 ca d1 8a 2d e6 42 00 00 08 00 00 00 00 " "00 00 00 00 00 00 00 00 89 00 03 44 89 00 03 44 55 48 00 18 01 00 e5 00 " "13 03 "; static const std::string noUHeSEL = "ESEL=" "00 00 df 00 00 00 00 20 00 04 07 5a 04 aa 00 00 50 48 00 30 01 00 e5 00 " "00 00 f6 ca c9 da 5b b7 00 00 f6 ca d1 8a 2d e6 42 00 00 08 00 00 00 00 " "00 00 00 00 00 00 00 00 89 00 03 44 89 00 03 44 00 00 00 18 01 00 e5 00 " "13 03 10"; // ESEL Severity bytes static const std::string SEV_RECOVERED = "10"; static const std::string SEV_PREDICTIVE = "20"; static const std::string SEV_UNRECOV = "40"; static const std::string SEV_CRITICAL = "50"; static const std::string SEV_DIAG = "60"; static constexpr auto json = R"( [ { "dtls":[ { "CEID":"ABCD1234", "mod":"", "msg":"Error ABCD1234" } ], "err":"xyz.openbmc_project.Error.Test1" }, { "dtls":[ { "CEID":"XYZ222", "mod":"", "msg":"Error XYZ222" } ], "err":"xyz.openbmc_project.Error.Test2" }, { "dtls":[ { "CEID":"AAAAAA", "mod":"mod1", "msg":"Error AAAAAA" }, { "CEID":"BBBBBB", "mod":"mod2", "msg":"Error BBBBBB" }, { "CEID":"CCCCCC", "mod":"mod3", "msg":"Error CCCCCC" } ], "err":"xyz.openbmc_project.Error.Test3" }, { "dtls":[ { "CEID":"DDDDDDDD", "mod":"I2C", "msg":"Error DDDDDDDD" }, { "CEID":"EEEEEEEE", "mod":"FSI", "msg":"Error EEEEEEEE" } ], "err":"xyz.openbmc_project.Error.Test4" }, { "dtls":[ { "CEID":"FFFFFFFF", "mod":"6D", "msg":"Error FFFFFFFF" } ], "err":"xyz.openbmc_project.Error.Test5" }, { "dtls":[ { "CEID":"GGGGGGGG", "mod":"RAIL_5", "msg":"Error GGGGGGGG" } ], "err":"xyz.openbmc_project.Error.Test6" }, { "dtls":[ { "CEID":"HHHHHHHH", "mod":"INPUT_42", "msg":"Error HHHHHHHH" } ], "err":"xyz.openbmc_project.Error.Test7" }, { "dtls":[ { "CEID":"IIIIIII", "mod":"/match/this/path", "msg":"Error IIIIIII" } ], "err":"xyz.openbmc_project.Error.Test8" }, { "dtls":[ { "CEID":"JJJJJJJJ", "mod":"/inventory/core0||Warning", "msg":"Error JJJJJJJJ" }, { "CEID":"KKKKKKKK", "mod":"/inventory/core1||Informational", "msg":"Error KKKKKKKK" }, { "CEID":"LLLLLLLL", "mod":"/inventory/core2||Critical", "msg":"Error LLLLLLLL" }, { "CEID":"MMMMMMMM", "mod":"/inventory/core3||Critical", "msg":"Error MMMMMMMM" }, { "CEID":"NNNNNNNN", "mod":"/inventory/core4||Critical", "msg":"Error NNNNNNNN" }, { "CEID":"OOOOOOOO", "mod":"/inventory/core5", "msg":"Error OOOOOOOO" }, { "CEID":"PPPPPPPP", "mod":"/inventory/core5||Critical", "msg":"Error PPPPPPPP" } ], "err":"org.open_power.Host.Error.Event" } ])"; /** * Helper class to write the above json to a file and then * remove it when the tests are over. */ class PolicyTableTest : public ::testing::Test { protected: virtual void SetUp() { char dir[] = {"./jsonTestXXXXXX"}; jsonDir = mkdtemp(dir); jsonFile = jsonDir / "policy.json"; std::ofstream f{jsonFile}; f << json; } virtual void TearDown() { fs::remove_all(jsonDir); } fs::path jsonDir; fs::path jsonFile; }; /** * Test finding entries in the policy table */ TEST_F(PolicyTableTest, TestTable) { policy::Table policy{jsonFile}; ASSERT_EQ(policy.isLoaded(), true); //////////////////////////////////// // Basic search, no modifier std::string err{"xyz.openbmc_project.Error.Test2"}; std::string mod; auto details = policy.find(err, mod); ASSERT_EQ(static_cast(details), true); if (details) { ASSERT_EQ((*details).get().ceid, "XYZ222"); ASSERT_EQ((*details).get().msg, "Error XYZ222"); } ///////////////////////////////////// // Not found err = "foo"; details = policy.find(err, mod); ASSERT_EQ(static_cast(details), false); ///////////////////////////////////// // Test with a modifier err = "xyz.openbmc_project.Error.Test3"; mod = "mod3"; details = policy.find(err, mod); ASSERT_EQ(static_cast(details), true); if (details) { ASSERT_EQ((*details).get().ceid, "CCCCCC"); ASSERT_EQ((*details).get().msg, "Error CCCCCC"); } } /** * Test policy::find() that uses the data from a property * map to find entries in the policy table. */ TEST_F(PolicyTableTest, TestFinder) { using namespace std::literals::string_literals; policy::Table policy{jsonFile}; ASSERT_EQ(policy.isLoaded(), true); // A basic search with no modifier { DbusPropertyMap testProperties{ {"Message"s, Value{"xyz.openbmc_project.Error.Test1"s}}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "ABCD1234"); ASSERT_EQ(std::get(values), "Error ABCD1234"); } // Use CALLOUT_INVENTORY_PATH from the AdditionalData property { std::vector ad{"FOO=BAR"s, "CALLOUT_INVENTORY_PATH=mod2"s}; DbusPropertyMap testProperties{ {"Message"s, Value{"xyz.openbmc_project.Error.Test3"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "BBBBBB"); ASSERT_EQ(std::get(values), "Error BBBBBB"); } // Use an I2C DEVICE_PATH from the AdditionalData property { std::vector ad{"FOO=BAR"s, "CALLOUT_DEVICE_PATH=/some/i2c/path"s}; DbusPropertyMap testProperties{ {"Message"s, Value{"xyz.openbmc_project.Error.Test4"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "DDDDDDDD"); ASSERT_EQ(std::get(values), "Error DDDDDDDD"); } // Use an FSI DEVICE_PATH from the AdditionalData property { std::vector ad{"FOO=BAR"s, "CALLOUT_DEVICE_PATH=/some/fsi/path"s}; DbusPropertyMap testProperties{ {"Message"s, Value{"xyz.openbmc_project.Error.Test4"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "EEEEEEEE"); ASSERT_EQ(std::get(values), "Error EEEEEEEE"); } // Use PROCEDURE from the AdditionalData property { std::vector ad{"FOO=BAR"s, "PROCEDURE=109"s}; DbusPropertyMap testProperties{ {"Message"s, Value{"xyz.openbmc_project.Error.Test5"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "FFFFFFFF"); ASSERT_EQ(std::get(values), "Error FFFFFFFF"); } // Use RAIL_NAME from the AdditionalData property { std::vector ad{"FOO=BAR"s, "RAIL_NAME=RAIL_5"s}; DbusPropertyMap testProperties{ {"Message"s, Value{"xyz.openbmc_project.Error.Test6"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "GGGGGGGG"); ASSERT_EQ(std::get(values), "Error GGGGGGGG"); } // Use INPUT_NAME from the AdditionalData property { std::vector ad{"FOO=BAR"s, "INPUT_NAME=INPUT_42"s}; DbusPropertyMap testProperties{ {"Message"s, Value{"xyz.openbmc_project.Error.Test7"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "HHHHHHHH"); ASSERT_EQ(std::get(values), "Error HHHHHHHH"); } // Test not finding an entry. { DbusPropertyMap testProperties{{"Message"s, Value{"hello world"s}}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), policy.defaultEID()); ASSERT_EQ(std::get(values), policy.defaultMsg()); } // Test that strange AdditionalData values don't break anything { std::vector ad{"FOO"s, "INPUT_NAME="s}; DbusPropertyMap testProperties{ {"Message"s, Value{"xyz.openbmc_project.Error.Test7"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), policy.defaultEID()); ASSERT_EQ(std::get(values), policy.defaultMsg()); } // Test a device path modifier match { std::vector ad{"CALLOUT_DEVICE_PATH=/match/this/path"s}; DbusPropertyMap testProperties{ {"Message"s, Value{"xyz.openbmc_project.Error.Test8"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "IIIIIII"); ASSERT_EQ(std::get(values), "Error IIIIIII"); } // Test a predictive SEL matches on 'callout||Warning' { std::vector ad{eSELBase + SEV_PREDICTIVE, "CALLOUT_INVENTORY_PATH=/inventory/core0"s}; DbusPropertyMap testProperties{ {"Message"s, Value{"org.open_power.Host.Error.Event"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "JJJJJJJJ"); ASSERT_EQ(std::get(values), "Error JJJJJJJJ"); } // Test a recovered SEL matches on 'callout||Informational' { std::vector ad{eSELBase + SEV_RECOVERED, "CALLOUT_INVENTORY_PATH=/inventory/core1"s}; DbusPropertyMap testProperties{ {"Message"s, Value{"org.open_power.Host.Error.Event"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "KKKKKKKK"); ASSERT_EQ(std::get(values), "Error KKKKKKKK"); } // Test a critical severity matches on 'callout||Critical' { std::vector ad{eSELBase + SEV_CRITICAL, "CALLOUT_INVENTORY_PATH=/inventory/core2"s}; DbusPropertyMap testProperties{ {"Message"s, Value{"org.open_power.Host.Error.Event"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "LLLLLLLL"); ASSERT_EQ(std::get(values), "Error LLLLLLLL"); } // Test an unrecoverable SEL matches on 'callout||Critical' { std::vector ad{eSELBase + SEV_UNRECOV, "CALLOUT_INVENTORY_PATH=/inventory/core3"s}; DbusPropertyMap testProperties{ {"Message"s, Value{"org.open_power.Host.Error.Event"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "MMMMMMMM"); ASSERT_EQ(std::get(values), "Error MMMMMMMM"); } // Test a Diagnostic SEL matches on 'callout||Critical' { std::vector ad{eSELBase + SEV_DIAG, "CALLOUT_INVENTORY_PATH=/inventory/core4"s}; DbusPropertyMap testProperties{ {"Message"s, Value{"org.open_power.Host.Error.Event"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "NNNNNNNN"); ASSERT_EQ(std::get(values), "Error NNNNNNNN"); } // Test a short eSEL still matches the normal callout { std::vector ad{eSELBase, "CALLOUT_INVENTORY_PATH=/inventory/core5"s}; DbusPropertyMap testProperties{ {"Message"s, Value{"org.open_power.Host.Error.Event"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "OOOOOOOO"); ASSERT_EQ(std::get(values), "Error OOOOOOOO"); } // Test an eSEL with no UH section still matches a normal callout { std::vector ad{noUHeSEL, "CALLOUT_INVENTORY_PATH=/inventory/core5"s}; DbusPropertyMap testProperties{ {"Message"s, Value{"org.open_power.Host.Error.Event"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "OOOOOOOO"); ASSERT_EQ(std::get(values), "Error OOOOOOOO"); } // Test a bad severity is still considered critical (by design) { std::vector ad{eSELBase + "ZZ", "CALLOUT_INVENTORY_PATH=/inventory/core5"s}; DbusPropertyMap testProperties{ {"Message"s, Value{"org.open_power.Host.Error.Event"s}}, {"AdditionalData"s, ad}}; auto values = policy::find(policy, testProperties); ASSERT_EQ(std::get(values), "PPPPPPPP"); ASSERT_EQ(std::get(values), "Error PPPPPPPP"); } }