blob: a96f661d8408bdb09536772d7a81d3a11cded54c [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
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +080018#include "json_utils.hpp"
Matt Spinler367144c2019-09-19 15:33:52 -050019#include "pel_types.hpp"
20#include "pel_values.hpp"
21
Matt Spinler367144c2019-09-19 15:33:52 -050022#include <phosphor-logging/log.hpp>
23
Patrick Williams2544b412022-10-04 08:41:06 -050024#include <fstream>
25
Matt Spinler367144c2019-09-19 15:33:52 -050026namespace openpower
27{
28namespace pels
29{
30namespace message
31{
32
33namespace pv = pel_values;
34namespace fs = std::filesystem;
35using namespace phosphor::logging;
36
37constexpr auto debugFilePath = "/etc/phosphor-logging/";
38
39namespace helper
40{
41
42uint8_t getSubsystem(const std::string& subsystemName)
43{
44 // Get the actual value to use in the PEL for the string name
45 auto ss = pv::findByName(subsystemName, pv::subsystemValues);
46 if (ss == pv::subsystemValues.end())
47 {
48 // Schema validation should be catching this.
49 log<level::ERR>("Invalid subsystem name used in message registry",
50 entry("SUBSYSTEM=%s", subsystemName.c_str()));
51
52 throw std::runtime_error("Invalid subsystem used in message registry");
53 }
54
55 return std::get<pv::fieldValuePos>(*ss);
56}
57
58uint8_t getSeverity(const std::string& severityName)
59{
60 auto s = pv::findByName(severityName, pv::severityValues);
61 if (s == pv::severityValues.end())
62 {
63 // Schema validation should be catching this.
64 log<level::ERR>("Invalid severity name used in message registry",
65 entry("SEVERITY=%s", severityName.c_str()));
66
67 throw std::runtime_error("Invalid severity used in message registry");
68 }
69
70 return std::get<pv::fieldValuePos>(*s);
71}
72
Matt Spinleraadccc82020-04-10 14:33:42 -050073std::vector<RegistrySeverity> getSeverities(const nlohmann::json& severity)
74{
75 std::vector<RegistrySeverity> severities;
76
77 // The plain string value, like "unrecoverable"
78 if (severity.is_string())
79 {
80 RegistrySeverity s;
81 s.severity = getSeverity(severity.get<std::string>());
82 severities.push_back(std::move(s));
83 }
84 else
85 {
86 // An array, with an element like:
87 // {
88 // "SevValue": "unrecoverable",
89 // "System", "systemA"
90 // }
91 for (const auto& sev : severity)
92 {
93 RegistrySeverity s;
94 s.severity = getSeverity(sev["SevValue"].get<std::string>());
95
96 if (sev.contains("System"))
97 {
98 s.system = sev["System"].get<std::string>();
99 }
100
101 severities.push_back(std::move(s));
102 }
103 }
104
105 return severities;
106}
107
Matt Spinler367144c2019-09-19 15:33:52 -0500108uint16_t getActionFlags(const std::vector<std::string>& flags)
109{
110 uint16_t actionFlags = 0;
111
112 // Make the bitmask based on the array of flag names
113 for (const auto& flag : flags)
114 {
115 auto s = pv::findByName(flag, pv::actionFlagsValues);
116 if (s == pv::actionFlagsValues.end())
117 {
118 // Schema validation should be catching this.
119 log<level::ERR>("Invalid action flag name used in message registry",
120 entry("FLAG=%s", flag.c_str()));
121
122 throw std::runtime_error(
123 "Invalid action flag used in message registry");
124 }
125
126 actionFlags |= std::get<pv::fieldValuePos>(*s);
127 }
128
129 return actionFlags;
130}
131
132uint8_t getEventType(const std::string& eventTypeName)
133{
134 auto t = pv::findByName(eventTypeName, pv::eventTypeValues);
135 if (t == pv::eventTypeValues.end())
136 {
137 log<level::ERR>("Invalid event type used in message registry",
138 entry("EVENT_TYPE=%s", eventTypeName.c_str()));
139
140 throw std::runtime_error("Invalid event type used in message registry");
141 }
142 return std::get<pv::fieldValuePos>(*t);
143}
144
145uint8_t getEventScope(const std::string& eventScopeName)
146{
147 auto s = pv::findByName(eventScopeName, pv::eventScopeValues);
148 if (s == pv::eventScopeValues.end())
149 {
150 log<level::ERR>("Invalid event scope used in registry",
151 entry("EVENT_SCOPE=%s", eventScopeName.c_str()));
152
153 throw std::runtime_error(
154 "Invalid event scope used in message registry");
155 }
156 return std::get<pv::fieldValuePos>(*s);
157}
158
Matt Spinler93e29322019-09-20 11:16:15 -0500159uint16_t getSRCReasonCode(const nlohmann::json& src, const std::string& name)
160{
161 std::string rc = src["ReasonCode"];
162 uint16_t reasonCode = strtoul(rc.c_str(), nullptr, 16);
163 if (reasonCode == 0)
164 {
165 log<phosphor::logging::level::ERR>(
166 "Invalid reason code in message registry",
167 entry("ERROR_NAME=%s", name.c_str()),
168 entry("REASON_CODE=%s", rc.c_str()));
169
170 throw std::runtime_error("Invalid reason code in message registry");
171 }
172 return reasonCode;
173}
174
175uint8_t getSRCType(const nlohmann::json& src, const std::string& name)
176{
177 // Looks like: "22"
178 std::string srcType = src["Type"];
179 size_t type = strtoul(srcType.c_str(), nullptr, 16);
180 if ((type == 0) || (srcType.size() != 2)) // 1 hex byte
181 {
182 log<phosphor::logging::level::ERR>(
183 "Invalid SRC Type in message registry",
184 entry("ERROR_NAME=%s", name.c_str()),
185 entry("SRC_TYPE=%s", srcType.c_str()));
186
187 throw std::runtime_error("Invalid SRC Type in message registry");
188 }
189
190 return type;
191}
192
Matt Spinler3fe93e92023-04-14 14:06:59 -0500193bool getSRCDeconfigFlag(const nlohmann::json& src)
194{
195 return src["DeconfigFlag"].get<bool>();
196}
197
Matt Spinlerda5b76b2023-06-01 15:56:57 -0500198bool getSRCCheckstopFlag(const nlohmann::json& src)
199{
200 return src["CheckstopFlag"].get<bool>();
201}
202
Matt Spinler93e29322019-09-20 11:16:15 -0500203std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
204 getSRCHexwordFields(const nlohmann::json& src, const std::string& name)
205{
206 std::map<SRC::WordNum, SRC::AdditionalDataField> hexwordFields;
207
208 // Build the map of which AdditionalData fields to use for which SRC words
209
210 // Like:
211 // {
212 // "8":
213 // {
214 // "AdditionalDataPropSource": "TEST"
215 // }
216 //
217 // }
218
219 for (const auto& word : src["Words6To9"].items())
220 {
221 std::string num = word.key();
222 size_t wordNum = std::strtoul(num.c_str(), nullptr, 10);
223
224 if (wordNum == 0)
225 {
226 log<phosphor::logging::level::ERR>(
227 "Invalid SRC word number in message registry",
228 entry("ERROR_NAME=%s", name.c_str()),
229 entry("SRC_WORD_NUM=%s", num.c_str()));
230
231 throw std::runtime_error("Invalid SRC word in message registry");
232 }
233
234 auto attributes = word.value();
Zane Shelleye8db29b2021-11-13 10:34:07 -0600235
236 // Use an empty string for the description if it does not exist.
237 auto itr = attributes.find("Description");
238 std::string desc = (attributes.end() != itr) ? *itr : "";
239
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800240 std::tuple<std::string, std::string> adPropSourceDesc(
Zane Shelleye8db29b2021-11-13 10:34:07 -0600241 attributes["AdditionalDataPropSource"], desc);
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800242 hexwordFields[wordNum] = std::move(adPropSourceDesc);
Matt Spinler93e29322019-09-20 11:16:15 -0500243 }
244
245 if (!hexwordFields.empty())
246 {
247 return hexwordFields;
248 }
249
250 return std::nullopt;
251}
252std::optional<std::vector<SRC::WordNum>>
253 getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name)
254{
255 std::vector<SRC::WordNum> symptomIDFields;
256
257 // Looks like:
258 // "SymptomIDFields": ["SRCWord3", "SRCWord6"],
259
Matt Spinler9d59d582021-05-19 07:57:10 -0600260 for (const std::string field : src["SymptomIDFields"])
Matt Spinler93e29322019-09-20 11:16:15 -0500261 {
262 // Just need the last digit off the end, e.g. SRCWord6.
263 // The schema enforces the format of these.
264 auto srcWordNum = field.substr(field.size() - 1);
265 size_t num = std::strtoul(srcWordNum.c_str(), nullptr, 10);
266 if (num == 0)
267 {
268 log<phosphor::logging::level::ERR>(
269 "Invalid symptom ID field in message registry",
270 entry("ERROR_NAME=%s", name.c_str()),
271 entry("FIELD_NAME=%s", srcWordNum.c_str()));
272
273 throw std::runtime_error("Invalid symptom ID in message registry");
274 }
275 symptomIDFields.push_back(num);
276 }
277 if (!symptomIDFields.empty())
278 {
279 return symptomIDFields;
280 }
281
282 return std::nullopt;
283}
284
285uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode,
286 const nlohmann::json& pelEntry, const std::string& name)
287{
288 uint16_t id = 0;
289
290 // If the ComponentID field is there, use that. Otherwise, if it's a
291 // 0xBD BMC error SRC, use the reasoncode.
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500292 if (pelEntry.contains("ComponentID"))
Matt Spinler93e29322019-09-20 11:16:15 -0500293 {
294 std::string componentID = pelEntry["ComponentID"];
295 id = strtoul(componentID.c_str(), nullptr, 16);
296 }
297 else
298 {
299 // On BMC error SRCs (BD), can just get the component ID from
300 // the first byte of the reason code.
301 if (srcType == static_cast<uint8_t>(SRCType::bmcError))
302 {
303 id = reasonCode & 0xFF00;
304 }
305 else
306 {
307 log<level::ERR>("Missing component ID field in message registry",
308 entry("ERROR_NAME=%s", name.c_str()));
309
310 throw std::runtime_error(
311 "Missing component ID field in message registry");
312 }
313 }
314
315 return id;
316}
317
Matt Spinler6b427cc2020-04-09 09:42:59 -0500318/**
319 * @brief Says if the JSON is the format that contains AdditionalData keys
320 * as in index into them.
321 *
322 * @param[in] json - The highest level callout JSON
323 *
324 * @return bool - If it is the AdditionalData format or not
325 */
326bool calloutUsesAdditionalData(const nlohmann::json& json)
327{
328 return (json.contains("ADName") &&
329 json.contains("CalloutsWithTheirADValues"));
330}
331
332/**
333 * @brief Finds the callouts to use when there is no AdditionalData,
334 * but the system type may be used as a key.
335 *
336 * One entry in the array looks like the following. The System key
337 * is optional and if not present it means that entry applies to
338 * every configuration that doesn't have another entry with a matching
339 * System key.
340 *
341 * {
342 * "System": "system1",
343 * "CalloutList":
344 * [
345 * {
346 * "Priority": "high",
347 * "LocCode": "P1-C1"
348 * },
349 * {
350 * "Priority": "low",
351 * "LocCode": "P1"
352 * }
353 * ]
354 * }
355 */
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500356const nlohmann::json&
357 findCalloutList(const nlohmann::json& json,
358 const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500359{
360 const nlohmann::json* callouts = nullptr;
361
362 if (!json.is_array())
363 {
364 throw std::runtime_error{"findCalloutList was not passed a JSON array"};
365 }
366
367 // The entry with the system type match will take precedence over the entry
368 // without any "System" field in it at all, which will match all other
369 // cases.
370 for (const auto& calloutList : json)
371 {
372 if (calloutList.contains("System"))
373 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500374 if (std::find(systemNames.begin(), systemNames.end(),
375 calloutList["System"].get<std::string>()) !=
376 systemNames.end())
Matt Spinler6b427cc2020-04-09 09:42:59 -0500377 {
378 callouts = &calloutList["CalloutList"];
379 break;
380 }
381 }
382 else
383 {
384 // Any entry with no System key
385 callouts = &calloutList["CalloutList"];
386 }
387 }
388
389 if (!callouts)
390 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500391 std::string types;
392 std::for_each(systemNames.begin(), systemNames.end(),
393 [&types](const auto& t) { types += t + '|'; });
Matt Spinler6b427cc2020-04-09 09:42:59 -0500394 log<level::WARNING>(
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500395 "No matching system name entry or default system name entry "
Matt Spinler6b427cc2020-04-09 09:42:59 -0500396 " for PEL callout list",
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500397 entry("SYSTEMNAMES=%s", types.c_str()));
Matt Spinler6b427cc2020-04-09 09:42:59 -0500398
399 throw std::runtime_error{
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500400 "Could not find a CalloutList JSON for this error and system name"};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500401 }
402
403 return *callouts;
404}
405
406/**
407 * @brief Creates a RegistryCallout based on the input JSON.
408 *
409 * The JSON looks like:
410 * {
411 * "Priority": "high",
412 * "LocCode": "E1"
413 * ...
414 * }
415 *
416 * Schema validation enforces what keys are present.
417 *
418 * @param[in] json - The JSON dictionary entry for a callout
419 *
420 * @return RegistryCallout - A filled in RegistryCallout
421 */
422RegistryCallout makeRegistryCallout(const nlohmann::json& json)
423{
424 RegistryCallout callout;
425
426 callout.priority = "high";
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500427 callout.useInventoryLocCode = false;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500428
429 if (json.contains("Priority"))
430 {
431 callout.priority = json["Priority"].get<std::string>();
432 }
433
434 if (json.contains("LocCode"))
435 {
436 callout.locCode = json["LocCode"].get<std::string>();
437 }
438
439 if (json.contains("Procedure"))
440 {
441 callout.procedure = json["Procedure"].get<std::string>();
442 }
443 else if (json.contains("SymbolicFRU"))
444 {
445 callout.symbolicFRU = json["SymbolicFRU"].get<std::string>();
446 }
447 else if (json.contains("SymbolicFRUTrusted"))
448 {
449 callout.symbolicFRUTrusted =
450 json["SymbolicFRUTrusted"].get<std::string>();
451 }
452
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500453 if (json.contains("UseInventoryLocCode"))
454 {
455 callout.useInventoryLocCode = json["UseInventoryLocCode"].get<bool>();
456 }
457
Matt Spinler6b427cc2020-04-09 09:42:59 -0500458 return callout;
459}
460
461/**
462 * @brief Returns the callouts to use when an AdditionalData key is
463 * required to find the correct entries.
464 *
465 * The System property is used to find which CalloutList to use.
466 * If System is missing, then that CalloutList is valid for
467 * everything.
468 *
469 * The JSON looks like:
470 * [
471 * {
472 * "System": "systemA",
473 * "CalloutList":
474 * [
475 * {
476 * "Priority": "high",
477 * "LocCode": "P1-C5"
478 * }
479 * ]
480 * }
481 * ]
482 *
483 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500484 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500485 *
486 * @return std::vector<RegistryCallout> - The callouts to use
487 */
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500488std::vector<RegistryCallout>
489 getCalloutsWithoutAD(const nlohmann::json& json,
490 const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500491{
492 std::vector<RegistryCallout> calloutEntries;
493
494 // Find the CalloutList to use based on the system type
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500495 const auto& calloutList = findCalloutList(json, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500496
497 // We finally found the callouts, make the objects.
498 for (const auto& callout : calloutList)
499 {
500 calloutEntries.push_back(std::move(makeRegistryCallout(callout)));
501 }
502
503 return calloutEntries;
504}
505
506/**
507 * @brief Returns the callouts to use when an AdditionalData key is
508 * required to find the correct entries.
509 *
510 * The JSON looks like:
511 * {
512 * "ADName": "PROC_NUM",
513 * "CalloutsWithTheirADValues":
514 * [
515 * {
516 * "ADValue": "0",
517 * "Callouts":
518 * [
519 * {
520 * "CalloutList":
521 * [
522 * {
523 * "Priority": "high",
524 * "LocCode": "P1-C5"
525 * }
526 * ]
527 * }
528 * ]
529 * }
530 * ]
531 * }
532 *
533 * Note that the "Callouts" entry above is the same as the top level
534 * entry used when there is no AdditionalData key.
535 *
536 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500537 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500538 * @param[in] additionalData - The AdditionalData property
539 *
540 * @return std::vector<RegistryCallout> - The callouts to use
541 */
542std::vector<RegistryCallout>
543 getCalloutsUsingAD(const nlohmann::json& json,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500544 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500545 const AdditionalData& additionalData)
546{
547 // This indicates which AD field we'll be using
548 auto keyName = json["ADName"].get<std::string>();
549
550 // Get the actual value from the AD data
551 auto adValue = additionalData.getValue(keyName);
552
553 if (!adValue)
554 {
555 // The AdditionalData did not contain the necessary key
556 log<level::WARNING>(
557 "The PEL message registry callouts JSON "
558 "said to use an AdditionalData key that isn't in the "
559 "AdditionalData event log property",
560 entry("ADNAME=%s\n", keyName.c_str()));
561 throw std::runtime_error{
562 "Missing AdditionalData entry for this callout"};
563 }
564
565 const auto& callouts = json["CalloutsWithTheirADValues"];
566
567 // find the entry with that AD value
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500568 auto it = std::find_if(callouts.begin(), callouts.end(),
569 [adValue](const nlohmann::json& j) {
570 return *adValue == j["ADValue"].get<std::string>();
571 });
Matt Spinler6b427cc2020-04-09 09:42:59 -0500572
573 if (it == callouts.end())
574 {
Matt Spinlerf397afc2021-01-29 11:21:44 -0600575 // This can happen if not all possible values were in the
Matt Spinler3d923312022-08-01 09:52:55 -0500576 // message registry and that's fine. There may be a
577 // "CalloutsWhenNoADMatch" section that contains callouts
578 // to use in this case.
579 if (json.contains("CalloutsWhenNoADMatch"))
580 {
581 return getCalloutsWithoutAD(json["CalloutsWhenNoADMatch"],
582 systemNames);
583 }
Matt Spinlerf397afc2021-01-29 11:21:44 -0600584 return std::vector<RegistryCallout>{};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500585 }
586
587 // Proceed to find the callouts possibly based on system type.
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500588 return getCalloutsWithoutAD((*it)["Callouts"], systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500589}
590
Matt Spinler711f1122022-12-15 11:41:20 -0600591/**
592 * @brief Returns the journal capture information
593 *
594 * The JSON looks like:
595 * "JournalCapture": {
596 * "NumLines": 30
597 * }
598 *
599 * "JournalCapture":
600 * {
601 * "Sections": [
602 * {
603 * "SyslogID": "phosphor-log-manager",
604 * "NumLines": 20
605 * }
606 * ]
607 * }
608 *
609 * @param json - The journal capture JSON
610 * @return JournalCapture - The filled in variant
611 */
612JournalCapture getJournalCapture(const nlohmann::json& json)
613{
614 JournalCapture capt;
615
616 // Primary key is either NumLines or Sections.
617 if (json.contains("NumLines"))
618 {
619 capt = json.at("NumLines").get<size_t>();
620 }
621 else if (json.contains("Sections"))
622 {
623 AppCaptureList captures;
624 for (const auto& capture : json.at("Sections"))
625 {
626 AppCapture ac;
627 ac.syslogID = capture.at("SyslogID").get<std::string>();
628 ac.numLines = capture.at("NumLines").get<size_t>();
629 captures.push_back(std::move(ac));
630 }
631
632 capt = captures;
633 }
634 else
635 {
636 log<level::ERR>("JournalCapture section not the right format");
637 throw std::runtime_error{"JournalCapture section not the right format"};
638 }
639
640 return capt;
641}
642
Matt Spinler367144c2019-09-19 15:33:52 -0500643} // namespace helper
644
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800645std::optional<Entry> Registry::lookup(const std::string& name, LookupType type,
646 bool toCache)
Matt Spinler367144c2019-09-19 15:33:52 -0500647{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800648 std::optional<nlohmann::json> registryTmp;
649 auto& registryOpt = (_registry) ? _registry : registryTmp;
650 if (!registryOpt)
Matt Spinler367144c2019-09-19 15:33:52 -0500651 {
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800652 registryOpt = readRegistry(_registryFile);
653 if (!registryOpt)
654 {
655 return std::nullopt;
656 }
657 else if (toCache)
658 {
659 // Save message registry in memory for peltool
660 _registry = std::move(registryTmp);
661 }
Matt Spinler367144c2019-09-19 15:33:52 -0500662 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800663 auto& reg = (_registry) ? _registry : registryTmp;
664 const auto& registry = reg.value();
Matt Spinler367144c2019-09-19 15:33:52 -0500665 // Find an entry with this name in the PEL array.
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500666 auto e = std::find_if(registry["PELs"].begin(), registry["PELs"].end(),
Matt Spinler81bc5612023-06-01 16:48:19 -0500667 [&name, &type](const nlohmann::json& j) {
668 return ((name == j.at("Name").get<std::string>() &&
669 type == LookupType::name) ||
670 (name == j.at("SRC").at("ReasonCode").get<std::string>() &&
671 type == LookupType::reasonCode));
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500672 });
Matt Spinler367144c2019-09-19 15:33:52 -0500673
674 if (e != registry["PELs"].end())
675 {
676 // Fill in the Entry structure from the JSON. Most, but not all, fields
677 // are optional.
678
679 try
680 {
681 Entry entry;
682 entry.name = (*e)["Name"];
Matt Spinler23970b02022-02-25 16:34:46 -0600683
684 if (e->contains("Subsystem"))
685 {
686 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]);
687 }
Matt Spinlere07f9152019-11-01 10:48:36 -0500688
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500689 if (e->contains("ActionFlags"))
Matt Spinlere07f9152019-11-01 10:48:36 -0500690 {
691 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]);
692 }
Matt Spinler367144c2019-09-19 15:33:52 -0500693
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500694 if (e->contains("MfgActionFlags"))
Matt Spinler367144c2019-09-19 15:33:52 -0500695 {
696 entry.mfgActionFlags =
697 helper::getActionFlags((*e)["MfgActionFlags"]);
698 }
699
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500700 if (e->contains("Severity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500701 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500702 entry.severity = helper::getSeverities((*e)["Severity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500703 }
704
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500705 if (e->contains("MfgSeverity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500706 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500707 entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500708 }
709
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500710 if (e->contains("EventType"))
Matt Spinler367144c2019-09-19 15:33:52 -0500711 {
712 entry.eventType = helper::getEventType((*e)["EventType"]);
713 }
714
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500715 if (e->contains("EventScope"))
Matt Spinler367144c2019-09-19 15:33:52 -0500716 {
717 entry.eventScope = helper::getEventScope((*e)["EventScope"]);
718 }
719
Matt Spinler93e29322019-09-20 11:16:15 -0500720 auto& src = (*e)["SRC"];
721 entry.src.reasonCode = helper::getSRCReasonCode(src, name);
722
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500723 if (src.contains("Type"))
Matt Spinler93e29322019-09-20 11:16:15 -0500724 {
725 entry.src.type = helper::getSRCType(src, name);
726 }
727 else
728 {
729 entry.src.type = static_cast<uint8_t>(SRCType::bmcError);
730 }
731
732 // Now that we know the SRC type and reason code,
733 // we can get the component ID.
734 entry.componentID = helper::getComponentID(
735 entry.src.type, entry.src.reasonCode, *e, name);
736
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500737 if (src.contains("Words6To9"))
Matt Spinler93e29322019-09-20 11:16:15 -0500738 {
Patrick Williams2544b412022-10-04 08:41:06 -0500739 entry.src.hexwordADFields = helper::getSRCHexwordFields(src,
740 name);
Matt Spinler93e29322019-09-20 11:16:15 -0500741 }
742
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500743 if (src.contains("SymptomIDFields"))
Matt Spinler93e29322019-09-20 11:16:15 -0500744 {
745 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
746 }
747
Matt Spinler3fe93e92023-04-14 14:06:59 -0500748 if (src.contains("DeconfigFlag"))
749 {
750 entry.src.deconfigFlag = helper::getSRCDeconfigFlag(src);
751 }
752
Matt Spinlerda5b76b2023-06-01 15:56:57 -0500753 if (src.contains("CheckstopFlag"))
754 {
755 entry.src.checkstopFlag = helper::getSRCCheckstopFlag(src);
756 }
757
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800758 auto& doc = (*e)["Documentation"];
759 entry.doc.message = doc["Message"];
760 entry.doc.description = doc["Description"];
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500761 if (doc.contains("MessageArgSources"))
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800762 {
Matt Spinler3bb15b92022-04-27 09:32:10 -0500763 entry.doc.messageArgSources = doc["MessageArgSources"];
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800764 }
765
Matt Spinlerd8e29002020-04-09 09:11:22 -0500766 // If there are callouts defined, save the JSON for later
767 if (_loadCallouts)
768 {
769 if (e->contains("Callouts"))
770 {
771 entry.callouts = (*e)["Callouts"];
772 }
773 else if (e->contains("CalloutsUsingAD"))
774 {
775 entry.callouts = (*e)["CalloutsUsingAD"];
776 }
777 }
778
Matt Spinler711f1122022-12-15 11:41:20 -0600779 if (e->contains("JournalCapture"))
780 {
781 entry.journalCapture =
782 helper::getJournalCapture((*e)["JournalCapture"]);
783 }
784
Matt Spinler367144c2019-09-19 15:33:52 -0500785 return entry;
786 }
Matt Spinler45796e82022-07-01 11:25:27 -0500787 catch (const std::exception& ex)
Matt Spinler367144c2019-09-19 15:33:52 -0500788 {
789 log<level::ERR>("Found invalid message registry field",
Matt Spinler45796e82022-07-01 11:25:27 -0500790 entry("ERROR=%s", ex.what()));
Matt Spinler367144c2019-09-19 15:33:52 -0500791 }
792 }
793
794 return std::nullopt;
795}
796
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800797std::optional<nlohmann::json>
798 Registry::readRegistry(const std::filesystem::path& registryFile)
799{
800 // Look in /etc first in case someone put a test file there
801 fs::path debugFile{fs::path{debugFilePath} / registryFileName};
802 nlohmann::json registry;
803 std::ifstream file;
804
805 if (fs::exists(debugFile))
806 {
807 log<level::INFO>("Using debug PEL message registry");
808 file.open(debugFile);
809 }
810 else
811 {
812 file.open(registryFile);
813 }
814
815 try
816 {
817 registry = nlohmann::json::parse(file);
818 }
Patrick Williams66491c62021-10-06 12:23:37 -0500819 catch (const std::exception& e)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800820 {
821 log<level::ERR>("Error parsing message registry JSON",
822 entry("JSON_ERROR=%s", e.what()));
823 return std::nullopt;
824 }
825 return registry;
826}
827
Matt Spinler6b427cc2020-04-09 09:42:59 -0500828std::vector<RegistryCallout>
829 Registry::getCallouts(const nlohmann::json& calloutJSON,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500830 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500831 const AdditionalData& additionalData)
832{
833 // The JSON may either use an AdditionalData key
834 // as an index, or not.
835 if (helper::calloutUsesAdditionalData(calloutJSON))
836 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500837 return helper::getCalloutsUsingAD(calloutJSON, systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500838 additionalData);
839 }
840
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500841 return helper::getCalloutsWithoutAD(calloutJSON, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500842}
843
Matt Spinler367144c2019-09-19 15:33:52 -0500844} // namespace message
845} // namespace pels
846} // namespace openpower