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