blob: 92c1b14e6389306acd91e16313ffcb95d21e602b [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 Spinler367144c2019-09-19 15:33:52 -050016#include "extensions/openpower-pels/registry.hpp"
17
18#include <filesystem>
19#include <fstream>
20#include <nlohmann/json.hpp>
21
22#include <gtest/gtest.h>
23
24using namespace openpower::pels::message;
Matt Spinler6b427cc2020-04-09 09:42:59 -050025using namespace openpower::pels;
Matt Spinler367144c2019-09-19 15:33:52 -050026namespace fs = std::filesystem;
27
28const auto registryData = R"(
29{
30 "PELs":
31 [
32 {
33 "Name": "xyz.openbmc_project.Power.Fault",
34 "Subsystem": "power_supply",
Matt Spinler367144c2019-09-19 15:33:52 -050035
36 "SRC":
37 {
38 "ReasonCode": "0x2030"
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +080039 },
40
41 "Documentation":
42 {
43 "Description": "A PGOOD Fault",
44 "Message": "PS had a PGOOD Fault"
Matt Spinler367144c2019-09-19 15:33:52 -050045 }
46 },
47
48 {
49 "Name": "xyz.openbmc_project.Power.OverVoltage",
50 "Subsystem": "power_control_hw",
51 "Severity": "unrecoverable",
52 "MfgSeverity": "non_error",
53 "ActionFlags": ["service_action", "report", "call_home"],
54 "MfgActionFlags": ["hidden"],
55
56 "SRC":
57 {
58 "ReasonCode": "0x2333",
59 "Type": "BD",
60 "SymptomIDFields": ["SRCWord5", "SRCWord6", "SRCWord7"],
61 "PowerFault": true,
62 "Words6To9":
63 {
64 "6":
65 {
66 "description": "Failing unit number",
67 "AdditionalDataPropSource": "PS_NUM"
68 },
69
70 "7":
71 {
72 "description": "bad voltage",
73 "AdditionalDataPropSource": "VOLTAGE"
74 }
75 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +080076 },
77
78 "Documentation":
79 {
80 "Description": "A PGOOD Fault",
81 "Message": "PS %1 had a PGOOD Fault",
82 "MessageArgSources":
83 [
84 "SRCWord6"
85 ],
86 "Notes": [
87 "In the UserData section there is a JSON",
88 "dump that provides debug information."
89 ]
Matt Spinler367144c2019-09-19 15:33:52 -050090 }
91 }
92 ]
93}
94)";
95
96class RegistryTest : public ::testing::Test
97{
98 protected:
99 static void SetUpTestCase()
100 {
101 char path[] = "/tmp/regtestXXXXXX";
102 regDir = mkdtemp(path);
103 }
104
105 static void TearDownTestCase()
106 {
107 fs::remove_all(regDir);
108 }
109
110 static std::string writeData(const char* data)
111 {
112 fs::path path = regDir / "registry.json";
113 std::ofstream stream{path};
114 stream << data;
115 return path;
116 }
117
118 static fs::path regDir;
119};
120
121fs::path RegistryTest::regDir{};
122
123TEST_F(RegistryTest, TestNoEntry)
124{
125 auto path = RegistryTest::writeData(registryData);
126 Registry registry{path};
127
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800128 auto entry = registry.lookup("foo", LookupType::name);
Matt Spinler367144c2019-09-19 15:33:52 -0500129 EXPECT_FALSE(entry);
130}
131
132TEST_F(RegistryTest, TestFindEntry)
133{
134 auto path = RegistryTest::writeData(registryData);
135 Registry registry{path};
136
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800137 auto entry = registry.lookup("xyz.openbmc_project.Power.OverVoltage",
138 LookupType::name);
Matt Spinler367144c2019-09-19 15:33:52 -0500139 ASSERT_TRUE(entry);
140 EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage");
141 EXPECT_EQ(entry->subsystem, 0x62);
142 EXPECT_EQ(*(entry->severity), 0x40);
143 EXPECT_EQ(*(entry->mfgSeverity), 0x00);
Matt Spinlere07f9152019-11-01 10:48:36 -0500144 EXPECT_EQ(*(entry->actionFlags), 0xA800);
Matt Spinler367144c2019-09-19 15:33:52 -0500145 EXPECT_EQ(*(entry->mfgActionFlags), 0x4000);
Matt Spinler93e29322019-09-20 11:16:15 -0500146 EXPECT_EQ(entry->componentID, 0x2300);
Matt Spinler367144c2019-09-19 15:33:52 -0500147 EXPECT_FALSE(entry->eventType);
148 EXPECT_FALSE(entry->eventScope);
149
Matt Spinler93e29322019-09-20 11:16:15 -0500150 EXPECT_EQ(entry->src.type, 0xBD);
151 EXPECT_EQ(entry->src.reasonCode, 0x2333);
152 EXPECT_EQ(*(entry->src.powerFault), true);
153
154 auto& hexwords = entry->src.hexwordADFields;
155 EXPECT_TRUE(hexwords);
156 EXPECT_EQ((*hexwords).size(), 2);
157
158 auto word = (*hexwords).find(6);
159 EXPECT_NE(word, (*hexwords).end());
160 EXPECT_EQ(word->second, "PS_NUM");
161
162 word = (*hexwords).find(7);
163 EXPECT_NE(word, (*hexwords).end());
164 EXPECT_EQ(word->second, "VOLTAGE");
165
166 auto& sid = entry->src.symptomID;
167 EXPECT_TRUE(sid);
168 EXPECT_EQ((*sid).size(), 3);
169 EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 5), (*sid).end());
170 EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 6), (*sid).end());
171 EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 7), (*sid).end());
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800172
173 EXPECT_EQ(entry->doc.description, "A PGOOD Fault");
174 EXPECT_EQ(entry->doc.message, "PS %1 had a PGOOD Fault");
175 auto& hexwordSource = entry->doc.messageArgSources;
176 EXPECT_TRUE(hexwordSource);
177 EXPECT_EQ((*hexwordSource).size(), 1);
178 EXPECT_EQ((*hexwordSource).front(), "SRCWord6");
179
180 entry = registry.lookup("0x2333", LookupType::reasonCode);
181 ASSERT_TRUE(entry);
182 EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage");
Matt Spinler367144c2019-09-19 15:33:52 -0500183}
184
185// Check the entry that mostly uses defaults
186TEST_F(RegistryTest, TestFindEntryMinimal)
187{
188 auto path = RegistryTest::writeData(registryData);
189 Registry registry{path};
190
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800191 auto entry =
192 registry.lookup("xyz.openbmc_project.Power.Fault", LookupType::name);
Matt Spinler367144c2019-09-19 15:33:52 -0500193 ASSERT_TRUE(entry);
194 EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.Fault");
195 EXPECT_EQ(entry->subsystem, 0x61);
196 EXPECT_FALSE(entry->severity);
197 EXPECT_FALSE(entry->mfgSeverity);
198 EXPECT_FALSE(entry->mfgActionFlags);
Matt Spinlere07f9152019-11-01 10:48:36 -0500199 EXPECT_FALSE(entry->actionFlags);
Matt Spinler93e29322019-09-20 11:16:15 -0500200 EXPECT_EQ(entry->componentID, 0x2000);
Matt Spinler367144c2019-09-19 15:33:52 -0500201 EXPECT_FALSE(entry->eventType);
202 EXPECT_FALSE(entry->eventScope);
Matt Spinler93e29322019-09-20 11:16:15 -0500203
204 EXPECT_EQ(entry->src.reasonCode, 0x2030);
205 EXPECT_EQ(entry->src.type, 0xBD);
206 EXPECT_FALSE(entry->src.powerFault);
207 EXPECT_FALSE(entry->src.hexwordADFields);
208 EXPECT_FALSE(entry->src.symptomID);
Matt Spinler367144c2019-09-19 15:33:52 -0500209}
210
211TEST_F(RegistryTest, TestBadJSON)
212{
213 auto path = RegistryTest::writeData("bad {} json");
214
215 Registry registry{path};
216
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800217 EXPECT_FALSE(registry.lookup("foo", LookupType::name));
Matt Spinler367144c2019-09-19 15:33:52 -0500218}
219
220// Test the helper functions the use the pel_values data.
221TEST_F(RegistryTest, TestHelperFunctions)
222{
223 using namespace openpower::pels::message::helper;
224 EXPECT_EQ(getSubsystem("input_power_source"), 0xA1);
225 EXPECT_THROW(getSubsystem("foo"), std::runtime_error);
226
227 EXPECT_EQ(getSeverity("symptom_recovered"), 0x71);
228 EXPECT_THROW(getSeverity("foo"), std::runtime_error);
229
230 EXPECT_EQ(getEventType("dump_notification"), 0x08);
231 EXPECT_THROW(getEventType("foo"), std::runtime_error);
232
233 EXPECT_EQ(getEventScope("possibly_multiple_platforms"), 0x04);
234 EXPECT_THROW(getEventScope("foo"), std::runtime_error);
235
236 std::vector<std::string> flags{"service_action", "dont_report",
237 "termination"};
238 EXPECT_EQ(getActionFlags(flags), 0x9100);
239
240 flags.clear();
241 flags.push_back("foo");
242 EXPECT_THROW(getActionFlags(flags), std::runtime_error);
243}
Matt Spinler93e29322019-09-20 11:16:15 -0500244
245TEST_F(RegistryTest, TestGetSRCReasonCode)
246{
247 using namespace openpower::pels::message::helper;
248 EXPECT_EQ(getSRCReasonCode(R"({"ReasonCode": "0x5555"})"_json, "foo"),
249 0x5555);
250
251 EXPECT_THROW(getSRCReasonCode(R"({"ReasonCode": "ZZZZ"})"_json, "foo"),
252 std::runtime_error);
253}
254
255TEST_F(RegistryTest, TestGetSRCType)
256{
257 using namespace openpower::pels::message::helper;
258 EXPECT_EQ(getSRCType(R"({"Type": "11"})"_json, "foo"), 0x11);
259 EXPECT_EQ(getSRCType(R"({"Type": "BF"})"_json, "foo"), 0xBF);
260
261 EXPECT_THROW(getSRCType(R"({"Type": "1"})"_json, "foo"),
262 std::runtime_error);
263
264 EXPECT_THROW(getSRCType(R"({"Type": "111"})"_json, "foo"),
265 std::runtime_error);
266}
267
268TEST_F(RegistryTest, TestGetSRCHexwordFields)
269{
270 using namespace openpower::pels::message::helper;
271 const auto hexwords = R"(
272 {"Words6To9":
273 {
274 "8":
275 {
276 "AdditionalDataPropSource": "TEST"
277 }
278 }
279 })"_json;
280
281 auto fields = getSRCHexwordFields(hexwords, "foo");
282 EXPECT_TRUE(fields);
283 auto word = fields->find(8);
284 EXPECT_NE(word, fields->end());
285
286 const auto theInvalidRWord = R"(
287 {"Words6To9":
288 {
289 "R":
290 {
291 "AdditionalDataPropSource": "TEST"
292 }
293 }
294 })"_json;
295
296 EXPECT_THROW(getSRCHexwordFields(theInvalidRWord, "foo"),
297 std::runtime_error);
298}
299
300TEST_F(RegistryTest, TestGetSRCSymptomIDFields)
301{
302 using namespace openpower::pels::message::helper;
303 const auto sID = R"(
304 {
305 "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord5"]
306 })"_json;
307
308 auto fields = getSRCSymptomIDFields(sID, "foo");
309 EXPECT_NE(std::find(fields->begin(), fields->end(), 3), fields->end());
310 EXPECT_NE(std::find(fields->begin(), fields->end(), 4), fields->end());
311 EXPECT_NE(std::find(fields->begin(), fields->end(), 5), fields->end());
312
313 const auto badField = R"(
314 {
315 "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord"]
316 })"_json;
317
318 EXPECT_THROW(getSRCSymptomIDFields(badField, "foo"), std::runtime_error);
319}
320
321TEST_F(RegistryTest, TestGetComponentID)
322{
323 using namespace openpower::pels::message::helper;
324
325 // Get it from the JSON
326 auto id =
327 getComponentID(0xBD, 0x4200, R"({"ComponentID":"0x4200"})"_json, "foo");
328 EXPECT_EQ(id, 0x4200);
329
330 // Get it from the reason code on a 0xBD SRC
331 id = getComponentID(0xBD, 0x6700, R"({})"_json, "foo");
332 EXPECT_EQ(id, 0x6700);
333
334 // Not present on a 0x11 SRC
335 EXPECT_THROW(getComponentID(0x11, 0x8800, R"({})"_json, "foo"),
336 std::runtime_error);
337}
Matt Spinler6b427cc2020-04-09 09:42:59 -0500338
339// Test when callouts are in the JSON.
340TEST_F(RegistryTest, TestGetCallouts)
341{
342 {
343 // Callouts without AD, that depend on system type,
344 // where there is a default entry without a system type.
345 auto json = R"(
346 [
347 {
348 "System": "system1",
349 "CalloutList":
350 [
351 {
352 "Priority": "high",
353 "LocCode": "P1-C1"
354 },
355 {
356 "Priority": "low",
357 "LocCode": "P1"
358 },
359 {
360 "Priority": "low",
361 "SymbolicFRU": "service_docs"
362 }
363 ]
364 },
365 {
366 "CalloutList":
367 [
368 {
369 "Priority": "medium",
370 "Procedure": "no_vpd_for_fru"
371 },
372 {
373 "Priority": "low",
374 "LocCode": "P3-C8",
375 "SymbolicFRUTrusted": "service_docs"
376 }
377 ]
378
379 }
380 ])"_json;
381
382 AdditionalData ad;
383
384 auto callouts = Registry::getCallouts(json, "system1", ad);
385 EXPECT_EQ(callouts.size(), 3);
386 EXPECT_EQ(callouts[0].priority, "high");
387 EXPECT_EQ(callouts[0].locCode, "P1-C1");
388 EXPECT_EQ(callouts[0].procedure, "");
389 EXPECT_EQ(callouts[0].symbolicFRU, "");
390 EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
391 EXPECT_EQ(callouts[1].priority, "low");
392 EXPECT_EQ(callouts[1].locCode, "P1");
393 EXPECT_EQ(callouts[1].procedure, "");
394 EXPECT_EQ(callouts[1].symbolicFRU, "");
395 EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
396 EXPECT_EQ(callouts[2].priority, "low");
397 EXPECT_EQ(callouts[2].locCode, "");
398 EXPECT_EQ(callouts[2].procedure, "");
399 EXPECT_EQ(callouts[2].symbolicFRU, "service_docs");
400 EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
401
402 // system2 isn't in the JSON, so it will pick the default one
403 callouts = Registry::getCallouts(json, "system2", ad);
404 EXPECT_EQ(callouts.size(), 2);
405 EXPECT_EQ(callouts[0].priority, "medium");
406 EXPECT_EQ(callouts[0].locCode, "");
407 EXPECT_EQ(callouts[0].procedure, "no_vpd_for_fru");
408 EXPECT_EQ(callouts[0].symbolicFRU, "");
409 EXPECT_EQ(callouts[1].priority, "low");
410 EXPECT_EQ(callouts[1].locCode, "P3-C8");
411 EXPECT_EQ(callouts[1].procedure, "");
412 EXPECT_EQ(callouts[1].symbolicFRU, "");
413 EXPECT_EQ(callouts[1].symbolicFRUTrusted, "service_docs");
414 }
415
416 // Empty JSON array (treated as an error)
417 {
418 auto json = R"([])"_json;
419 AdditionalData ad;
420 EXPECT_THROW(Registry::getCallouts(json, "system1", ad),
421 std::runtime_error);
422 }
423
424 {
425 // Callouts without AD, that depend on system type,
426 // where there isn't a default entry without a system type.
427 auto json = R"(
428 [
429 {
430 "System": "system1",
431 "CalloutList":
432 [
433 {
434 "Priority": "high",
435 "LocCode": "P1-C1"
436 },
437 {
438 "Priority": "low",
439 "LocCode": "P1",
440 "SymbolicFRU": "1234567"
441 }
442 ]
443 },
444 {
445 "System": "system2",
446 "CalloutList":
447 [
448 {
449 "Priority": "medium",
450 "LocCode": "P7",
451 "CalloutType": "tool_fru"
452 }
453 ]
454
455 }
456 ])"_json;
457
458 AdditionalData ad;
459
460 auto callouts = Registry::getCallouts(json, "system1", ad);
461 EXPECT_EQ(callouts.size(), 2);
462 EXPECT_EQ(callouts[0].priority, "high");
463 EXPECT_EQ(callouts[0].locCode, "P1-C1");
464 EXPECT_EQ(callouts[0].procedure, "");
465 EXPECT_EQ(callouts[0].symbolicFRU, "");
466 EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
467 EXPECT_EQ(callouts[1].priority, "low");
468 EXPECT_EQ(callouts[1].locCode, "P1");
469 EXPECT_EQ(callouts[1].procedure, "");
470 EXPECT_EQ(callouts[1].symbolicFRU, "1234567");
471 EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
472
473 callouts = Registry::getCallouts(json, "system2", ad);
474 EXPECT_EQ(callouts.size(), 1);
475 EXPECT_EQ(callouts[0].priority, "medium");
476 EXPECT_EQ(callouts[0].locCode, "P7");
477 EXPECT_EQ(callouts[0].procedure, "");
478 EXPECT_EQ(callouts[0].symbolicFRU, "");
479 EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
480
481 // There is no entry for system3 or a default system,
482 // so this should fail.
483 EXPECT_THROW(Registry::getCallouts(json, "system3", ad),
484 std::runtime_error);
485 }
486
487 {
488 // Callouts that use the AdditionalData key PROC_NUM
489 // as an index into them, along with a system type.
490 // It supports PROC_NUMs 0 and 1.
491 auto json = R"(
492 {
493 "ADName": "PROC_NUM",
494 "CalloutsWithTheirADValues":
495 [
496 {
497 "ADValue": "0",
498 "Callouts":
499 [
500 {
501 "System": "system3",
502 "CalloutList":
503 [
504 {
505 "Priority": "high",
506 "LocCode": "P1-C5"
507 },
508 {
509 "Priority": "medium",
510 "LocCode": "P1-C6",
511 "SymbolicFRU": "1234567"
512 },
513 {
514 "Priority": "low",
515 "Procedure": "no_vpd_for_fru",
516 "CalloutType": "config_procedure"
517 }
518 ]
519 },
520 {
521 "CalloutList":
522 [
523 {
524 "Priority": "low",
525 "LocCode": "P55"
526 }
527 ]
528 }
529 ]
530 },
531 {
532 "ADValue": "1",
533 "Callouts":
534 [
535 {
536 "CalloutList":
537 [
538 {
539 "Priority": "high",
540 "LocCode": "P1-C6",
541 "CalloutType": "external_fru"
542 }
543 ]
544 }
545 ]
546 }
547 ]
548 })"_json;
549
550 {
551 // Find callouts for PROC_NUM 0 on system3
552 std::vector<std::string> adData{"PROC_NUM=0"};
553 AdditionalData ad{adData};
554
555 auto callouts = Registry::getCallouts(json, "system3", ad);
556 EXPECT_EQ(callouts.size(), 3);
557 EXPECT_EQ(callouts[0].priority, "high");
558 EXPECT_EQ(callouts[0].locCode, "P1-C5");
559 EXPECT_EQ(callouts[0].procedure, "");
560 EXPECT_EQ(callouts[0].symbolicFRU, "");
561 EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
562 EXPECT_EQ(callouts[1].priority, "medium");
563 EXPECT_EQ(callouts[1].locCode, "P1-C6");
564 EXPECT_EQ(callouts[1].procedure, "");
565 EXPECT_EQ(callouts[1].symbolicFRU, "1234567");
566 EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
567 EXPECT_EQ(callouts[2].priority, "low");
568 EXPECT_EQ(callouts[2].locCode, "");
569 EXPECT_EQ(callouts[2].procedure, "no_vpd_for_fru");
570 EXPECT_EQ(callouts[2].symbolicFRU, "");
571 EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
572
573 // Find callouts for PROC_NUM 0 that uses the default system entry.
574 callouts = Registry::getCallouts(json, "system99", ad);
575 EXPECT_EQ(callouts.size(), 1);
576 EXPECT_EQ(callouts[0].priority, "low");
577 EXPECT_EQ(callouts[0].locCode, "P55");
578 EXPECT_EQ(callouts[0].procedure, "");
579 EXPECT_EQ(callouts[0].symbolicFRU, "");
580 EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
581 }
582 {
583 // Find callouts for PROC_NUM 1 that uses a default system entry.
584 std::vector<std::string> adData{"PROC_NUM=1"};
585 AdditionalData ad{adData};
586
587 auto callouts = Registry::getCallouts(json, "system1", ad);
588 EXPECT_EQ(callouts.size(), 1);
589 EXPECT_EQ(callouts[0].priority, "high");
590 EXPECT_EQ(callouts[0].locCode, "P1-C6");
591 EXPECT_EQ(callouts[0].procedure, "");
592 EXPECT_EQ(callouts[0].symbolicFRU, "");
593 EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
594 }
595 {
596 // There is no entry for PROC_NUM 2, it will fail.
597 std::vector<std::string> adData{"PROC_NUM=2"};
598 AdditionalData ad{adData};
599
600 EXPECT_THROW(Registry::getCallouts(json, "system1", ad),
601 std::runtime_error);
602 }
603 }
604}