xref: /openbmc/ibm-logging/test/test_policy.cpp (revision c57aa4b9)
1 /**
2  * Copyright © 2018 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 "policy_find.hpp"
17 #include "policy_table.hpp"
18 
19 #include <experimental/filesystem>
20 #include <fstream>
21 
22 #include <gtest/gtest.h>
23 
24 using namespace ibm::logging;
25 namespace fs = std::experimental::filesystem;
26 
27 static constexpr auto HOST_EVENT = "org.open_power.Host.Error.Event";
28 
29 // ESEL contents all of the way up to right before the severity
30 // byte in the UH section
31 static const std::string eSELBase =
32     "ESEL="
33     "00 00 df 00 00 00 00 20 00 04 07 5a 04 aa 00 00 50 48 00 30 01 00 e5 00 "
34     "00 00 f6 ca c9 da 5b b7 00 00 f6 ca d1 8a 2d e6 42 00 00 08 00 00 00 00 "
35     "00 00 00 00 00 00 00 00 89 00 03 44 89 00 03 44 55 48 00 18 01 00 e5 00 "
36     "13 03 ";
37 
38 static const std::string noUHeSEL =
39     "ESEL="
40     "00 00 df 00 00 00 00 20 00 04 07 5a 04 aa 00 00 50 48 00 30 01 00 e5 00 "
41     "00 00 f6 ca c9 da 5b b7 00 00 f6 ca d1 8a 2d e6 42 00 00 08 00 00 00 00 "
42     "00 00 00 00 00 00 00 00 89 00 03 44 89 00 03 44 00 00 00 18 01 00 e5 00 "
43     "13 03 10";
44 
45 // ESEL Severity bytes
46 static const std::string SEV_RECOVERED = "10";
47 static const std::string SEV_PREDICTIVE = "20";
48 static const std::string SEV_UNRECOV = "40";
49 static const std::string SEV_CRITICAL = "50";
50 static const std::string SEV_DIAG = "60";
51 
52 static constexpr auto json = R"(
53 [
54     {
55     "dtls":[
56       {
57         "CEID":"ABCD1234",
58         "mod":"",
59         "msg":"Error ABCD1234"
60       }
61     ],
62     "err":"xyz.openbmc_project.Error.Test1"
63     },
64 
65     {
66     "dtls":[
67       {
68         "CEID":"XYZ222",
69         "mod":"",
70         "msg":"Error XYZ222"
71       }
72     ],
73     "err":"xyz.openbmc_project.Error.Test2"
74     },
75 
76     {
77     "dtls":[
78       {
79         "CEID":"AAAAAA",
80         "mod":"mod1",
81         "msg":"Error AAAAAA"
82       },
83       {
84         "CEID":"BBBBBB",
85         "mod":"mod2",
86         "msg":"Error BBBBBB"
87       },
88       {
89         "CEID":"CCCCCC",
90         "mod":"mod3",
91         "msg":"Error CCCCCC"
92       }
93     ],
94     "err":"xyz.openbmc_project.Error.Test3"
95     },
96 
97     {
98     "dtls":[
99       {
100         "CEID":"DDDDDDDD",
101         "mod":"I2C",
102         "msg":"Error DDDDDDDD"
103       },
104       {
105         "CEID":"EEEEEEEE",
106         "mod":"FSI",
107         "msg":"Error EEEEEEEE"
108       }
109     ],
110     "err":"xyz.openbmc_project.Error.Test4"
111     },
112 
113     {
114     "dtls":[
115       {
116         "CEID":"FFFFFFFF",
117         "mod":"6D",
118         "msg":"Error FFFFFFFF"
119       }
120     ],
121     "err":"xyz.openbmc_project.Error.Test5"
122     },
123 
124     {
125     "dtls":[
126       {
127         "CEID":"GGGGGGGG",
128         "mod":"RAIL_5",
129         "msg":"Error GGGGGGGG"
130       }
131     ],
132     "err":"xyz.openbmc_project.Error.Test6"
133     },
134 
135     {
136     "dtls":[
137       {
138         "CEID":"HHHHHHHH",
139         "mod":"INPUT_42",
140         "msg":"Error HHHHHHHH"
141       }
142     ],
143     "err":"xyz.openbmc_project.Error.Test7"
144     },
145 
146     {
147     "dtls":[
148       {
149         "CEID":"IIIIIII",
150         "mod":"/match/this/path",
151         "msg":"Error IIIIIII"
152       }
153     ],
154     "err":"xyz.openbmc_project.Error.Test8"
155     },
156 
157     {
158     "dtls":[
159       {
160         "CEID":"JJJJJJJJ",
161         "mod":"/inventory/core0||Warning",
162         "msg":"Error JJJJJJJJ"
163       },
164       {
165         "CEID":"KKKKKKKK",
166         "mod":"/inventory/core1||Informational",
167         "msg":"Error KKKKKKKK"
168       },
169       {
170         "CEID":"LLLLLLLL",
171         "mod":"/inventory/core2||Critical",
172         "msg":"Error LLLLLLLL"
173       },
174       {
175         "CEID":"MMMMMMMM",
176         "mod":"/inventory/core3||Critical",
177         "msg":"Error MMMMMMMM"
178       },
179       {
180         "CEID":"NNNNNNNN",
181         "mod":"/inventory/core4||Critical",
182         "msg":"Error NNNNNNNN"
183       },
184       {
185         "CEID":"OOOOOOOO",
186         "mod":"/inventory/core5",
187         "msg":"Error OOOOOOOO"
188       },
189       {
190         "CEID":"PPPPPPPP",
191         "mod":"/inventory/core5||Critical",
192         "msg":"Error PPPPPPPP"
193       }
194     ],
195     "err":"org.open_power.Host.Error.Event"
196     }
197 ])";
198 
199 /**
200  * Helper class to write the above json to a file and then
201  * remove it when the tests are over.
202  */
203 class PolicyTableTest : public ::testing::Test
204 {
205   protected:
SetUp()206     virtual void SetUp()
207     {
208         char dir[] = {"./jsonTestXXXXXX"};
209 
210         jsonDir = mkdtemp(dir);
211         jsonFile = jsonDir / "policy.json";
212 
213         std::ofstream f{jsonFile};
214         f << json;
215     }
216 
TearDown()217     virtual void TearDown()
218     {
219         fs::remove_all(jsonDir);
220     }
221 
222     fs::path jsonDir;
223     fs::path jsonFile;
224 };
225 
226 /**
227  * Test finding entries in the policy table
228  */
TEST_F(PolicyTableTest,TestTable)229 TEST_F(PolicyTableTest, TestTable)
230 {
231     policy::Table policy{jsonFile};
232     ASSERT_EQ(policy.isLoaded(), true);
233 
234     ////////////////////////////////////
235     // Basic search, no modifier
236     std::string err{"xyz.openbmc_project.Error.Test2"};
237     std::string mod;
238 
239     auto details = policy.find(err, mod);
240     ASSERT_EQ(static_cast<bool>(details), true);
241     if (details)
242     {
243         ASSERT_EQ((*details).get().ceid, "XYZ222");
244         ASSERT_EQ((*details).get().msg, "Error XYZ222");
245     }
246 
247     /////////////////////////////////////
248     // Not found
249     err = "foo";
250     details = policy.find(err, mod);
251     ASSERT_EQ(static_cast<bool>(details), false);
252 
253     /////////////////////////////////////
254     // Test with a modifier
255     err = "xyz.openbmc_project.Error.Test3";
256     mod = "mod3";
257 
258     details = policy.find(err, mod);
259     ASSERT_EQ(static_cast<bool>(details), true);
260     if (details)
261     {
262         ASSERT_EQ((*details).get().ceid, "CCCCCC");
263         ASSERT_EQ((*details).get().msg, "Error CCCCCC");
264     }
265 }
266 
267 /**
268  * Test policy::find() that uses the data from a property
269  * map to find entries in the policy table.
270  */
TEST_F(PolicyTableTest,TestFinder)271 TEST_F(PolicyTableTest, TestFinder)
272 {
273     using namespace std::literals::string_literals;
274 
275     policy::Table policy{jsonFile};
276     ASSERT_EQ(policy.isLoaded(), true);
277 
278     // A basic search with no modifier
279     {
280         DbusPropertyMap testProperties{
281             {"Message"s, Value{"xyz.openbmc_project.Error.Test1"s}}};
282 
283         auto values = policy::find(policy, testProperties);
284         ASSERT_EQ(std::get<policy::EIDField>(values), "ABCD1234");
285         ASSERT_EQ(std::get<policy::MsgField>(values), "Error ABCD1234");
286     }
287 
288     // Use CALLOUT_INVENTORY_PATH from the AdditionalData property
289     {
290         std::vector<std::string> ad{"FOO=BAR"s, "CALLOUT_INVENTORY_PATH=mod2"s};
291         DbusPropertyMap testProperties{
292             {"Message"s, Value{"xyz.openbmc_project.Error.Test3"s}},
293             {"AdditionalData"s, ad}};
294 
295         auto values = policy::find(policy, testProperties);
296         ASSERT_EQ(std::get<policy::EIDField>(values), "BBBBBB");
297         ASSERT_EQ(std::get<policy::MsgField>(values), "Error BBBBBB");
298     }
299 
300     // Use an I2C DEVICE_PATH from the AdditionalData property
301     {
302         std::vector<std::string> ad{"FOO=BAR"s,
303                                     "CALLOUT_DEVICE_PATH=/some/i2c/path"s};
304         DbusPropertyMap testProperties{
305             {"Message"s, Value{"xyz.openbmc_project.Error.Test4"s}},
306             {"AdditionalData"s, ad}};
307 
308         auto values = policy::find(policy, testProperties);
309         ASSERT_EQ(std::get<policy::EIDField>(values), "DDDDDDDD");
310         ASSERT_EQ(std::get<policy::MsgField>(values), "Error DDDDDDDD");
311     }
312 
313     // Use an FSI DEVICE_PATH from the AdditionalData property
314     {
315         std::vector<std::string> ad{"FOO=BAR"s,
316                                     "CALLOUT_DEVICE_PATH=/some/fsi/path"s};
317         DbusPropertyMap testProperties{
318             {"Message"s, Value{"xyz.openbmc_project.Error.Test4"s}},
319             {"AdditionalData"s, ad}};
320 
321         auto values = policy::find(policy, testProperties);
322         ASSERT_EQ(std::get<policy::EIDField>(values), "EEEEEEEE");
323         ASSERT_EQ(std::get<policy::MsgField>(values), "Error EEEEEEEE");
324     }
325 
326     // Use PROCEDURE from the AdditionalData property
327     {
328         std::vector<std::string> ad{"FOO=BAR"s, "PROCEDURE=109"s};
329         DbusPropertyMap testProperties{
330             {"Message"s, Value{"xyz.openbmc_project.Error.Test5"s}},
331             {"AdditionalData"s, ad}};
332 
333         auto values = policy::find(policy, testProperties);
334         ASSERT_EQ(std::get<policy::EIDField>(values), "FFFFFFFF");
335         ASSERT_EQ(std::get<policy::MsgField>(values), "Error FFFFFFFF");
336     }
337 
338     // Use RAIL_NAME from the AdditionalData property
339     {
340         std::vector<std::string> ad{"FOO=BAR"s, "RAIL_NAME=RAIL_5"s};
341         DbusPropertyMap testProperties{
342             {"Message"s, Value{"xyz.openbmc_project.Error.Test6"s}},
343             {"AdditionalData"s, ad}};
344 
345         auto values = policy::find(policy, testProperties);
346         ASSERT_EQ(std::get<policy::EIDField>(values), "GGGGGGGG");
347         ASSERT_EQ(std::get<policy::MsgField>(values), "Error GGGGGGGG");
348     }
349 
350     // Use INPUT_NAME from the AdditionalData property
351     {
352         std::vector<std::string> ad{"FOO=BAR"s, "INPUT_NAME=INPUT_42"s};
353         DbusPropertyMap testProperties{
354             {"Message"s, Value{"xyz.openbmc_project.Error.Test7"s}},
355             {"AdditionalData"s, ad}};
356 
357         auto values = policy::find(policy, testProperties);
358         ASSERT_EQ(std::get<policy::EIDField>(values), "HHHHHHHH");
359         ASSERT_EQ(std::get<policy::MsgField>(values), "Error HHHHHHHH");
360     }
361 
362     // Test not finding an entry.
363     {
364         DbusPropertyMap testProperties{{"Message"s, Value{"hello world"s}}};
365 
366         auto values = policy::find(policy, testProperties);
367         ASSERT_EQ(std::get<policy::EIDField>(values), policy.defaultEID());
368         ASSERT_EQ(std::get<policy::MsgField>(values), policy.defaultMsg());
369     }
370 
371     // Test that strange AdditionalData values don't break anything
372     {
373         std::vector<std::string> ad{"FOO"s, "INPUT_NAME="s};
374         DbusPropertyMap testProperties{
375             {"Message"s, Value{"xyz.openbmc_project.Error.Test7"s}},
376             {"AdditionalData"s, ad}};
377 
378         auto values = policy::find(policy, testProperties);
379         ASSERT_EQ(std::get<policy::EIDField>(values), policy.defaultEID());
380         ASSERT_EQ(std::get<policy::MsgField>(values), policy.defaultMsg());
381     }
382 
383     // Test a device path modifier match
384     {
385         std::vector<std::string> ad{"CALLOUT_DEVICE_PATH=/match/this/path"s};
386         DbusPropertyMap testProperties{
387             {"Message"s, Value{"xyz.openbmc_project.Error.Test8"s}},
388             {"AdditionalData"s, ad}};
389 
390         auto values = policy::find(policy, testProperties);
391         ASSERT_EQ(std::get<policy::EIDField>(values), "IIIIIII");
392         ASSERT_EQ(std::get<policy::MsgField>(values), "Error IIIIIII");
393     }
394 
395     // Test a predictive SEL matches on 'callout||Warning'
396     {
397         std::vector<std::string> ad{eSELBase + SEV_PREDICTIVE,
398                                     "CALLOUT_INVENTORY_PATH=/inventory/core0"s};
399         DbusPropertyMap testProperties{
400             {"Message"s, Value{"org.open_power.Host.Error.Event"s}},
401             {"AdditionalData"s, ad}};
402 
403         auto values = policy::find(policy, testProperties);
404         ASSERT_EQ(std::get<policy::EIDField>(values), "JJJJJJJJ");
405         ASSERT_EQ(std::get<policy::MsgField>(values), "Error JJJJJJJJ");
406     }
407 
408     // Test a recovered SEL matches on 'callout||Informational'
409     {
410         std::vector<std::string> ad{eSELBase + SEV_RECOVERED,
411                                     "CALLOUT_INVENTORY_PATH=/inventory/core1"s};
412         DbusPropertyMap testProperties{
413             {"Message"s, Value{"org.open_power.Host.Error.Event"s}},
414             {"AdditionalData"s, ad}};
415 
416         auto values = policy::find(policy, testProperties);
417         ASSERT_EQ(std::get<policy::EIDField>(values), "KKKKKKKK");
418         ASSERT_EQ(std::get<policy::MsgField>(values), "Error KKKKKKKK");
419     }
420 
421     // Test a critical severity matches on 'callout||Critical'
422     {
423         std::vector<std::string> ad{eSELBase + SEV_CRITICAL,
424                                     "CALLOUT_INVENTORY_PATH=/inventory/core2"s};
425         DbusPropertyMap testProperties{
426             {"Message"s, Value{"org.open_power.Host.Error.Event"s}},
427             {"AdditionalData"s, ad}};
428 
429         auto values = policy::find(policy, testProperties);
430         ASSERT_EQ(std::get<policy::EIDField>(values), "LLLLLLLL");
431         ASSERT_EQ(std::get<policy::MsgField>(values), "Error LLLLLLLL");
432     }
433 
434     // Test an unrecoverable SEL matches on 'callout||Critical'
435     {
436         std::vector<std::string> ad{eSELBase + SEV_UNRECOV,
437                                     "CALLOUT_INVENTORY_PATH=/inventory/core3"s};
438         DbusPropertyMap testProperties{
439             {"Message"s, Value{"org.open_power.Host.Error.Event"s}},
440             {"AdditionalData"s, ad}};
441 
442         auto values = policy::find(policy, testProperties);
443         ASSERT_EQ(std::get<policy::EIDField>(values), "MMMMMMMM");
444         ASSERT_EQ(std::get<policy::MsgField>(values), "Error MMMMMMMM");
445     }
446 
447     // Test a Diagnostic SEL matches on 'callout||Critical'
448     {
449         std::vector<std::string> ad{eSELBase + SEV_DIAG,
450                                     "CALLOUT_INVENTORY_PATH=/inventory/core4"s};
451         DbusPropertyMap testProperties{
452             {"Message"s, Value{"org.open_power.Host.Error.Event"s}},
453             {"AdditionalData"s, ad}};
454 
455         auto values = policy::find(policy, testProperties);
456         ASSERT_EQ(std::get<policy::EIDField>(values), "NNNNNNNN");
457         ASSERT_EQ(std::get<policy::MsgField>(values), "Error NNNNNNNN");
458     }
459 
460     // Test a short eSEL still matches the normal callout
461     {
462         std::vector<std::string> ad{eSELBase,
463                                     "CALLOUT_INVENTORY_PATH=/inventory/core5"s};
464         DbusPropertyMap testProperties{
465             {"Message"s, Value{"org.open_power.Host.Error.Event"s}},
466             {"AdditionalData"s, ad}};
467 
468         auto values = policy::find(policy, testProperties);
469         ASSERT_EQ(std::get<policy::EIDField>(values), "OOOOOOOO");
470         ASSERT_EQ(std::get<policy::MsgField>(values), "Error OOOOOOOO");
471     }
472 
473     // Test an eSEL with no UH section still matches a normal callout
474     {
475         std::vector<std::string> ad{noUHeSEL,
476                                     "CALLOUT_INVENTORY_PATH=/inventory/core5"s};
477         DbusPropertyMap testProperties{
478             {"Message"s, Value{"org.open_power.Host.Error.Event"s}},
479             {"AdditionalData"s, ad}};
480 
481         auto values = policy::find(policy, testProperties);
482         ASSERT_EQ(std::get<policy::EIDField>(values), "OOOOOOOO");
483         ASSERT_EQ(std::get<policy::MsgField>(values), "Error OOOOOOOO");
484     }
485 
486     // Test a bad severity is still considered critical (by design)
487     {
488         std::vector<std::string> ad{eSELBase + "ZZ",
489                                     "CALLOUT_INVENTORY_PATH=/inventory/core5"s};
490         DbusPropertyMap testProperties{
491             {"Message"s, Value{"org.open_power.Host.Error.Event"s}},
492             {"AdditionalData"s, ad}};
493 
494         auto values = policy::find(policy, testProperties);
495         ASSERT_EQ(std::get<policy::EIDField>(values), "PPPPPPPP");
496         ASSERT_EQ(std::get<policy::MsgField>(values), "Error PPPPPPPP");
497     }
498 }
499