blob: c8f10d792c3ba55f305390837b5d0323e8ce10ad [file] [log] [blame]
Matt Spinler711d51d2019-11-06 09:36:51 -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 "registry.hpp"
17
18#include "pel_types.hpp"
19#include "pel_values.hpp"
20
21#include <fstream>
22#include <phosphor-logging/log.hpp>
23
24namespace openpower
25{
26namespace pels
27{
28namespace message
29{
30
31namespace pv = pel_values;
32namespace fs = std::filesystem;
33using namespace phosphor::logging;
34
35constexpr auto debugFilePath = "/etc/phosphor-logging/";
36
37namespace helper
38{
39
40uint8_t getSubsystem(const std::string& subsystemName)
41{
42 // Get the actual value to use in the PEL for the string name
43 auto ss = pv::findByName(subsystemName, pv::subsystemValues);
44 if (ss == pv::subsystemValues.end())
45 {
46 // Schema validation should be catching this.
47 log<level::ERR>("Invalid subsystem name used in message registry",
48 entry("SUBSYSTEM=%s", subsystemName.c_str()));
49
50 throw std::runtime_error("Invalid subsystem used in message registry");
51 }
52
53 return std::get<pv::fieldValuePos>(*ss);
54}
55
56uint8_t getSeverity(const std::string& severityName)
57{
58 auto s = pv::findByName(severityName, pv::severityValues);
59 if (s == pv::severityValues.end())
60 {
61 // Schema validation should be catching this.
62 log<level::ERR>("Invalid severity name used in message registry",
63 entry("SEVERITY=%s", severityName.c_str()));
64
65 throw std::runtime_error("Invalid severity used in message registry");
66 }
67
68 return std::get<pv::fieldValuePos>(*s);
69}
70
Matt Spinleraadccc82020-04-10 14:33:42 -050071std::vector<RegistrySeverity> getSeverities(const nlohmann::json& severity)
72{
73 std::vector<RegistrySeverity> severities;
74
75 // The plain string value, like "unrecoverable"
76 if (severity.is_string())
77 {
78 RegistrySeverity s;
79 s.severity = getSeverity(severity.get<std::string>());
80 severities.push_back(std::move(s));
81 }
82 else
83 {
84 // An array, with an element like:
85 // {
86 // "SevValue": "unrecoverable",
87 // "System", "systemA"
88 // }
89 for (const auto& sev : severity)
90 {
91 RegistrySeverity s;
92 s.severity = getSeverity(sev["SevValue"].get<std::string>());
93
94 if (sev.contains("System"))
95 {
96 s.system = sev["System"].get<std::string>();
97 }
98
99 severities.push_back(std::move(s));
100 }
101 }
102
103 return severities;
104}
105
Matt Spinler367144c2019-09-19 15:33:52 -0500106uint16_t getActionFlags(const std::vector<std::string>& flags)
107{
108 uint16_t actionFlags = 0;
109
110 // Make the bitmask based on the array of flag names
111 for (const auto& flag : flags)
112 {
113 auto s = pv::findByName(flag, pv::actionFlagsValues);
114 if (s == pv::actionFlagsValues.end())
115 {
116 // Schema validation should be catching this.
117 log<level::ERR>("Invalid action flag name used in message registry",
118 entry("FLAG=%s", flag.c_str()));
119
120 throw std::runtime_error(
121 "Invalid action flag used in message registry");
122 }
123
124 actionFlags |= std::get<pv::fieldValuePos>(*s);
125 }
126
127 return actionFlags;
128}
129
130uint8_t getEventType(const std::string& eventTypeName)
131{
132 auto t = pv::findByName(eventTypeName, pv::eventTypeValues);
133 if (t == pv::eventTypeValues.end())
134 {
135 log<level::ERR>("Invalid event type used in message registry",
136 entry("EVENT_TYPE=%s", eventTypeName.c_str()));
137
138 throw std::runtime_error("Invalid event type used in message registry");
139 }
140 return std::get<pv::fieldValuePos>(*t);
141}
142
143uint8_t getEventScope(const std::string& eventScopeName)
144{
145 auto s = pv::findByName(eventScopeName, pv::eventScopeValues);
146 if (s == pv::eventScopeValues.end())
147 {
148 log<level::ERR>("Invalid event scope used in registry",
149 entry("EVENT_SCOPE=%s", eventScopeName.c_str()));
150
151 throw std::runtime_error(
152 "Invalid event scope used in message registry");
153 }
154 return std::get<pv::fieldValuePos>(*s);
155}
156
Matt Spinler93e29322019-09-20 11:16:15 -0500157uint16_t getSRCReasonCode(const nlohmann::json& src, const std::string& name)
158{
159 std::string rc = src["ReasonCode"];
160 uint16_t reasonCode = strtoul(rc.c_str(), nullptr, 16);
161 if (reasonCode == 0)
162 {
163 log<phosphor::logging::level::ERR>(
164 "Invalid reason code in message registry",
165 entry("ERROR_NAME=%s", name.c_str()),
166 entry("REASON_CODE=%s", rc.c_str()));
167
168 throw std::runtime_error("Invalid reason code in message registry");
169 }
170 return reasonCode;
171}
172
173uint8_t getSRCType(const nlohmann::json& src, const std::string& name)
174{
175 // Looks like: "22"
176 std::string srcType = src["Type"];
177 size_t type = strtoul(srcType.c_str(), nullptr, 16);
178 if ((type == 0) || (srcType.size() != 2)) // 1 hex byte
179 {
180 log<phosphor::logging::level::ERR>(
181 "Invalid SRC Type in message registry",
182 entry("ERROR_NAME=%s", name.c_str()),
183 entry("SRC_TYPE=%s", srcType.c_str()));
184
185 throw std::runtime_error("Invalid SRC Type in message registry");
186 }
187
188 return type;
189}
190
191std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
192 getSRCHexwordFields(const nlohmann::json& src, const std::string& name)
193{
194 std::map<SRC::WordNum, SRC::AdditionalDataField> hexwordFields;
195
196 // Build the map of which AdditionalData fields to use for which SRC words
197
198 // Like:
199 // {
200 // "8":
201 // {
202 // "AdditionalDataPropSource": "TEST"
203 // }
204 //
205 // }
206
207 for (const auto& word : src["Words6To9"].items())
208 {
209 std::string num = word.key();
210 size_t wordNum = std::strtoul(num.c_str(), nullptr, 10);
211
212 if (wordNum == 0)
213 {
214 log<phosphor::logging::level::ERR>(
215 "Invalid SRC word number in message registry",
216 entry("ERROR_NAME=%s", name.c_str()),
217 entry("SRC_WORD_NUM=%s", num.c_str()));
218
219 throw std::runtime_error("Invalid SRC word in message registry");
220 }
221
222 auto attributes = word.value();
223 std::string adPropName = attributes["AdditionalDataPropSource"];
224 hexwordFields[wordNum] = std::move(adPropName);
225 }
226
227 if (!hexwordFields.empty())
228 {
229 return hexwordFields;
230 }
231
232 return std::nullopt;
233}
234std::optional<std::vector<SRC::WordNum>>
235 getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name)
236{
237 std::vector<SRC::WordNum> symptomIDFields;
238
239 // Looks like:
240 // "SymptomIDFields": ["SRCWord3", "SRCWord6"],
241
242 for (const std::string& field : src["SymptomIDFields"])
243 {
244 // Just need the last digit off the end, e.g. SRCWord6.
245 // The schema enforces the format of these.
246 auto srcWordNum = field.substr(field.size() - 1);
247 size_t num = std::strtoul(srcWordNum.c_str(), nullptr, 10);
248 if (num == 0)
249 {
250 log<phosphor::logging::level::ERR>(
251 "Invalid symptom ID field in message registry",
252 entry("ERROR_NAME=%s", name.c_str()),
253 entry("FIELD_NAME=%s", srcWordNum.c_str()));
254
255 throw std::runtime_error("Invalid symptom ID in message registry");
256 }
257 symptomIDFields.push_back(num);
258 }
259 if (!symptomIDFields.empty())
260 {
261 return symptomIDFields;
262 }
263
264 return std::nullopt;
265}
266
267uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode,
268 const nlohmann::json& pelEntry, const std::string& name)
269{
270 uint16_t id = 0;
271
272 // If the ComponentID field is there, use that. Otherwise, if it's a
273 // 0xBD BMC error SRC, use the reasoncode.
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500274 if (pelEntry.contains("ComponentID"))
Matt Spinler93e29322019-09-20 11:16:15 -0500275 {
276 std::string componentID = pelEntry["ComponentID"];
277 id = strtoul(componentID.c_str(), nullptr, 16);
278 }
279 else
280 {
281 // On BMC error SRCs (BD), can just get the component ID from
282 // the first byte of the reason code.
283 if (srcType == static_cast<uint8_t>(SRCType::bmcError))
284 {
285 id = reasonCode & 0xFF00;
286 }
287 else
288 {
289 log<level::ERR>("Missing component ID field in message registry",
290 entry("ERROR_NAME=%s", name.c_str()));
291
292 throw std::runtime_error(
293 "Missing component ID field in message registry");
294 }
295 }
296
297 return id;
298}
299
Matt Spinler6b427cc2020-04-09 09:42:59 -0500300/**
301 * @brief Says if the JSON is the format that contains AdditionalData keys
302 * as in index into them.
303 *
304 * @param[in] json - The highest level callout JSON
305 *
306 * @return bool - If it is the AdditionalData format or not
307 */
308bool calloutUsesAdditionalData(const nlohmann::json& json)
309{
310 return (json.contains("ADName") &&
311 json.contains("CalloutsWithTheirADValues"));
312}
313
314/**
315 * @brief Finds the callouts to use when there is no AdditionalData,
316 * but the system type may be used as a key.
317 *
318 * One entry in the array looks like the following. The System key
319 * is optional and if not present it means that entry applies to
320 * every configuration that doesn't have another entry with a matching
321 * System key.
322 *
323 * {
324 * "System": "system1",
325 * "CalloutList":
326 * [
327 * {
328 * "Priority": "high",
329 * "LocCode": "P1-C1"
330 * },
331 * {
332 * "Priority": "low",
333 * "LocCode": "P1"
334 * }
335 * ]
336 * }
337 */
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500338const nlohmann::json&
339 findCalloutList(const nlohmann::json& json,
340 const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500341{
342 const nlohmann::json* callouts = nullptr;
343
344 if (!json.is_array())
345 {
346 throw std::runtime_error{"findCalloutList was not passed a JSON array"};
347 }
348
349 // The entry with the system type match will take precedence over the entry
350 // without any "System" field in it at all, which will match all other
351 // cases.
352 for (const auto& calloutList : json)
353 {
354 if (calloutList.contains("System"))
355 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500356 if (std::find(systemNames.begin(), systemNames.end(),
357 calloutList["System"].get<std::string>()) !=
358 systemNames.end())
Matt Spinler6b427cc2020-04-09 09:42:59 -0500359 {
360 callouts = &calloutList["CalloutList"];
361 break;
362 }
363 }
364 else
365 {
366 // Any entry with no System key
367 callouts = &calloutList["CalloutList"];
368 }
369 }
370
371 if (!callouts)
372 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500373 std::string types;
374 std::for_each(systemNames.begin(), systemNames.end(),
375 [&types](const auto& t) { types += t + '|'; });
Matt Spinler6b427cc2020-04-09 09:42:59 -0500376 log<level::WARNING>(
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500377 "No matching system name entry or default system name entry "
Matt Spinler6b427cc2020-04-09 09:42:59 -0500378 " for PEL callout list",
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500379 entry("SYSTEMNAMES=%s", types.c_str()));
Matt Spinler6b427cc2020-04-09 09:42:59 -0500380
381 throw std::runtime_error{
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500382 "Could not find a CalloutList JSON for this error and system name"};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500383 }
384
385 return *callouts;
386}
387
388/**
389 * @brief Creates a RegistryCallout based on the input JSON.
390 *
391 * The JSON looks like:
392 * {
393 * "Priority": "high",
394 * "LocCode": "E1"
395 * ...
396 * }
397 *
398 * Schema validation enforces what keys are present.
399 *
400 * @param[in] json - The JSON dictionary entry for a callout
401 *
402 * @return RegistryCallout - A filled in RegistryCallout
403 */
404RegistryCallout makeRegistryCallout(const nlohmann::json& json)
405{
406 RegistryCallout callout;
407
408 callout.priority = "high";
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500409 callout.useInventoryLocCode = false;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500410
411 if (json.contains("Priority"))
412 {
413 callout.priority = json["Priority"].get<std::string>();
414 }
415
416 if (json.contains("LocCode"))
417 {
418 callout.locCode = json["LocCode"].get<std::string>();
419 }
420
421 if (json.contains("Procedure"))
422 {
423 callout.procedure = json["Procedure"].get<std::string>();
424 }
425 else if (json.contains("SymbolicFRU"))
426 {
427 callout.symbolicFRU = json["SymbolicFRU"].get<std::string>();
428 }
429 else if (json.contains("SymbolicFRUTrusted"))
430 {
431 callout.symbolicFRUTrusted =
432 json["SymbolicFRUTrusted"].get<std::string>();
433 }
434
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500435 if (json.contains("UseInventoryLocCode"))
436 {
437 callout.useInventoryLocCode = json["UseInventoryLocCode"].get<bool>();
438 }
439
Matt Spinler6b427cc2020-04-09 09:42:59 -0500440 return callout;
441}
442
443/**
444 * @brief Returns the callouts to use when an AdditionalData key is
445 * required to find the correct entries.
446 *
447 * The System property is used to find which CalloutList to use.
448 * If System is missing, then that CalloutList is valid for
449 * everything.
450 *
451 * The JSON looks like:
452 * [
453 * {
454 * "System": "systemA",
455 * "CalloutList":
456 * [
457 * {
458 * "Priority": "high",
459 * "LocCode": "P1-C5"
460 * }
461 * ]
462 * }
463 * ]
464 *
465 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500466 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500467 *
468 * @return std::vector<RegistryCallout> - The callouts to use
469 */
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500470std::vector<RegistryCallout>
471 getCalloutsWithoutAD(const nlohmann::json& json,
472 const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500473{
474 std::vector<RegistryCallout> calloutEntries;
475
476 // Find the CalloutList to use based on the system type
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500477 const auto& calloutList = findCalloutList(json, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500478
479 // We finally found the callouts, make the objects.
480 for (const auto& callout : calloutList)
481 {
482 calloutEntries.push_back(std::move(makeRegistryCallout(callout)));
483 }
484
485 return calloutEntries;
486}
487
488/**
489 * @brief Returns the callouts to use when an AdditionalData key is
490 * required to find the correct entries.
491 *
492 * The JSON looks like:
493 * {
494 * "ADName": "PROC_NUM",
495 * "CalloutsWithTheirADValues":
496 * [
497 * {
498 * "ADValue": "0",
499 * "Callouts":
500 * [
501 * {
502 * "CalloutList":
503 * [
504 * {
505 * "Priority": "high",
506 * "LocCode": "P1-C5"
507 * }
508 * ]
509 * }
510 * ]
511 * }
512 * ]
513 * }
514 *
515 * Note that the "Callouts" entry above is the same as the top level
516 * entry used when there is no AdditionalData key.
517 *
518 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500519 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500520 * @param[in] additionalData - The AdditionalData property
521 *
522 * @return std::vector<RegistryCallout> - The callouts to use
523 */
524std::vector<RegistryCallout>
525 getCalloutsUsingAD(const nlohmann::json& json,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500526 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500527 const AdditionalData& additionalData)
528{
529 // This indicates which AD field we'll be using
530 auto keyName = json["ADName"].get<std::string>();
531
532 // Get the actual value from the AD data
533 auto adValue = additionalData.getValue(keyName);
534
535 if (!adValue)
536 {
537 // The AdditionalData did not contain the necessary key
538 log<level::WARNING>(
539 "The PEL message registry callouts JSON "
540 "said to use an AdditionalData key that isn't in the "
541 "AdditionalData event log property",
542 entry("ADNAME=%s\n", keyName.c_str()));
543 throw std::runtime_error{
544 "Missing AdditionalData entry for this callout"};
545 }
546
547 const auto& callouts = json["CalloutsWithTheirADValues"];
548
549 // find the entry with that AD value
550 auto it = std::find_if(
551 callouts.begin(), callouts.end(), [adValue](const nlohmann::json& j) {
552 return *adValue == j["ADValue"].get<std::string>();
553 });
554
555 if (it == callouts.end())
556 {
557 log<level::WARNING>(
558 "No callout entry found for the AdditionalData value used",
559 entry("AD_VALUE=%s", adValue->c_str()));
560
561 throw std::runtime_error{
562 "No callout entry found for the AdditionalData value used"};
563 }
564
565 // Proceed to find the callouts possibly based on system type.
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500566 return getCalloutsWithoutAD((*it)["Callouts"], systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500567}
568
Matt Spinler367144c2019-09-19 15:33:52 -0500569} // namespace helper
570
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800571std::optional<Entry> Registry::lookup(const std::string& name, LookupType type,
572 bool toCache)
Matt Spinler367144c2019-09-19 15:33:52 -0500573{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800574 std::optional<nlohmann::json> registryTmp;
575 auto& registryOpt = (_registry) ? _registry : registryTmp;
576 if (!registryOpt)
Matt Spinler367144c2019-09-19 15:33:52 -0500577 {
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800578 registryOpt = readRegistry(_registryFile);
579 if (!registryOpt)
580 {
581 return std::nullopt;
582 }
583 else if (toCache)
584 {
585 // Save message registry in memory for peltool
586 _registry = std::move(registryTmp);
587 }
Matt Spinler367144c2019-09-19 15:33:52 -0500588 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800589 auto& reg = (_registry) ? _registry : registryTmp;
590 const auto& registry = reg.value();
Matt Spinler367144c2019-09-19 15:33:52 -0500591 // Find an entry with this name in the PEL array.
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800592 auto e = std::find_if(
593 registry["PELs"].begin(), registry["PELs"].end(),
594 [&name, &type](const auto& j) {
595 return ((name == j["Name"] && type == LookupType::name) ||
596 (name == j["SRC"]["ReasonCode"] &&
597 type == LookupType::reasonCode));
598 });
Matt Spinler367144c2019-09-19 15:33:52 -0500599
600 if (e != registry["PELs"].end())
601 {
602 // Fill in the Entry structure from the JSON. Most, but not all, fields
603 // are optional.
604
605 try
606 {
607 Entry entry;
608 entry.name = (*e)["Name"];
609 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]);
Matt Spinlere07f9152019-11-01 10:48:36 -0500610
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500611 if (e->contains("ActionFlags"))
Matt Spinlere07f9152019-11-01 10:48:36 -0500612 {
613 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]);
614 }
Matt Spinler367144c2019-09-19 15:33:52 -0500615
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500616 if (e->contains("MfgActionFlags"))
Matt Spinler367144c2019-09-19 15:33:52 -0500617 {
618 entry.mfgActionFlags =
619 helper::getActionFlags((*e)["MfgActionFlags"]);
620 }
621
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500622 if (e->contains("Severity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500623 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500624 entry.severity = helper::getSeverities((*e)["Severity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500625 }
626
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500627 if (e->contains("MfgSeverity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500628 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500629 entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500630 }
631
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500632 if (e->contains("EventType"))
Matt Spinler367144c2019-09-19 15:33:52 -0500633 {
634 entry.eventType = helper::getEventType((*e)["EventType"]);
635 }
636
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500637 if (e->contains("EventScope"))
Matt Spinler367144c2019-09-19 15:33:52 -0500638 {
639 entry.eventScope = helper::getEventScope((*e)["EventScope"]);
640 }
641
Matt Spinler93e29322019-09-20 11:16:15 -0500642 auto& src = (*e)["SRC"];
643 entry.src.reasonCode = helper::getSRCReasonCode(src, name);
644
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500645 if (src.contains("Type"))
Matt Spinler93e29322019-09-20 11:16:15 -0500646 {
647 entry.src.type = helper::getSRCType(src, name);
648 }
649 else
650 {
651 entry.src.type = static_cast<uint8_t>(SRCType::bmcError);
652 }
653
654 // Now that we know the SRC type and reason code,
655 // we can get the component ID.
656 entry.componentID = helper::getComponentID(
657 entry.src.type, entry.src.reasonCode, *e, name);
658
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500659 if (src.contains("Words6To9"))
Matt Spinler93e29322019-09-20 11:16:15 -0500660 {
661 entry.src.hexwordADFields =
662 helper::getSRCHexwordFields(src, name);
663 }
664
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500665 if (src.contains("SymptomIDFields"))
Matt Spinler93e29322019-09-20 11:16:15 -0500666 {
667 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
668 }
669
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500670 if (src.contains("PowerFault"))
Matt Spinler93e29322019-09-20 11:16:15 -0500671 {
672 entry.src.powerFault = src["PowerFault"];
673 }
Matt Spinler367144c2019-09-19 15:33:52 -0500674
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800675 auto& doc = (*e)["Documentation"];
676 entry.doc.message = doc["Message"];
677 entry.doc.description = doc["Description"];
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500678 if (doc.contains("MessageArgSources"))
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800679 {
680 entry.doc.messageArgSources = doc["MessageArgSources"];
681 }
682
Matt Spinlerd8e29002020-04-09 09:11:22 -0500683 // If there are callouts defined, save the JSON for later
684 if (_loadCallouts)
685 {
686 if (e->contains("Callouts"))
687 {
688 entry.callouts = (*e)["Callouts"];
689 }
690 else if (e->contains("CalloutsUsingAD"))
691 {
692 entry.callouts = (*e)["CalloutsUsingAD"];
693 }
694 }
695
Matt Spinler367144c2019-09-19 15:33:52 -0500696 return entry;
697 }
698 catch (std::exception& e)
699 {
700 log<level::ERR>("Found invalid message registry field",
701 entry("ERROR=%s", e.what()));
702 }
703 }
704
705 return std::nullopt;
706}
707
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800708std::optional<nlohmann::json>
709 Registry::readRegistry(const std::filesystem::path& registryFile)
710{
711 // Look in /etc first in case someone put a test file there
712 fs::path debugFile{fs::path{debugFilePath} / registryFileName};
713 nlohmann::json registry;
714 std::ifstream file;
715
716 if (fs::exists(debugFile))
717 {
718 log<level::INFO>("Using debug PEL message registry");
719 file.open(debugFile);
720 }
721 else
722 {
723 file.open(registryFile);
724 }
725
726 try
727 {
728 registry = nlohmann::json::parse(file);
729 }
730 catch (std::exception& e)
731 {
732 log<level::ERR>("Error parsing message registry JSON",
733 entry("JSON_ERROR=%s", e.what()));
734 return std::nullopt;
735 }
736 return registry;
737}
738
Matt Spinler6b427cc2020-04-09 09:42:59 -0500739std::vector<RegistryCallout>
740 Registry::getCallouts(const nlohmann::json& calloutJSON,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500741 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500742 const AdditionalData& additionalData)
743{
744 // The JSON may either use an AdditionalData key
745 // as an index, or not.
746 if (helper::calloutUsesAdditionalData(calloutJSON))
747 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500748 return helper::getCalloutsUsingAD(calloutJSON, systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500749 additionalData);
750 }
751
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500752 return helper::getCalloutsWithoutAD(calloutJSON, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500753}
754
Matt Spinler367144c2019-09-19 15:33:52 -0500755} // namespace message
756} // namespace pels
757} // namespace openpower