xref: /openbmc/phosphor-logging/extensions/openpower-pels/registry.cpp (revision 8a09b982ddeb0c1e13190d9cd196e06a778d6140)
1 /**
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  */
16 #include "registry.hpp"
17 
18 #include "pel_types.hpp"
19 #include "pel_values.hpp"
20 
21 #include <phosphor-logging/lg2.hpp>
22 
23 #include <algorithm>
24 #include <fstream>
25 
26 namespace openpower
27 {
28 namespace pels
29 {
30 namespace message
31 {
32 
33 namespace pv = pel_values;
34 namespace fs = std::filesystem;
35 
36 constexpr auto debugFilePath = "/etc/phosphor-logging/";
37 
38 namespace helper
39 {
40 
getSubsystem(const std::string & subsystemName)41 uint8_t getSubsystem(const std::string& subsystemName)
42 {
43     // Get the actual value to use in the PEL for the string name
44     auto ss = pv::findByName(subsystemName, pv::subsystemValues);
45     if (ss == pv::subsystemValues.end())
46     {
47         // Schema validation should be catching this.
48         lg2::error("Invalid subsystem name used in message registry: {SUBSYS}",
49                    "SUBSYS", subsystemName);
50 
51         throw std::runtime_error("Invalid subsystem used in message registry");
52     }
53 
54     return std::get<pv::fieldValuePos>(*ss);
55 }
56 
getSeverity(const std::string & severityName)57 uint8_t getSeverity(const std::string& severityName)
58 {
59     auto s = pv::findByName(severityName, pv::severityValues);
60     if (s == pv::severityValues.end())
61     {
62         // Schema validation should be catching this.
63         lg2::error("Invalid severity name used in message registry: {SEV}",
64                    "SEV", severityName);
65 
66         throw std::runtime_error("Invalid severity used in message registry");
67     }
68 
69     return std::get<pv::fieldValuePos>(*s);
70 }
71 
getSeverities(const nlohmann::json & severity)72 std::vector<RegistrySeverity> getSeverities(const nlohmann::json& severity)
73 {
74     std::vector<RegistrySeverity> severities;
75 
76     // The plain string value, like "unrecoverable"
77     if (severity.is_string())
78     {
79         RegistrySeverity s;
80         s.severity = getSeverity(severity.get<std::string>());
81         severities.push_back(std::move(s));
82     }
83     else
84     {
85         // An array, with an element like:
86         // {
87         //    "SevValue": "unrecoverable",
88         //    "System", "systemA"
89         // }
90         for (const auto& sev : severity)
91         {
92             RegistrySeverity s;
93             s.severity = getSeverity(sev["SevValue"].get<std::string>());
94 
95             if (sev.contains("System"))
96             {
97                 s.system = sev["System"].get<std::string>();
98             }
99 
100             severities.push_back(std::move(s));
101         }
102     }
103 
104     return severities;
105 }
106 
getActionFlags(const std::vector<std::string> & flags)107 uint16_t getActionFlags(const std::vector<std::string>& flags)
108 {
109     uint16_t actionFlags = 0;
110 
111     // Make the bitmask based on the array of flag names
112     for (const auto& flag : flags)
113     {
114         auto s = pv::findByName(flag, pv::actionFlagsValues);
115         if (s == pv::actionFlagsValues.end())
116         {
117             // Schema validation should be catching this.
118             lg2::error(
119                 "Invalid action flag name used in message registry: {FLAG}",
120                 "FLAG", flag);
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 
getEventType(const std::string & eventTypeName)132 uint8_t getEventType(const std::string& eventTypeName)
133 {
134     auto t = pv::findByName(eventTypeName, pv::eventTypeValues);
135     if (t == pv::eventTypeValues.end())
136     {
137         lg2::error("Invalid event type used in message registry: {TYPE}",
138                    "TYPE", eventTypeName);
139 
140         throw std::runtime_error("Invalid event type used in message registry");
141     }
142     return std::get<pv::fieldValuePos>(*t);
143 }
144 
getEventScope(const std::string & eventScopeName)145 uint8_t getEventScope(const std::string& eventScopeName)
146 {
147     auto s = pv::findByName(eventScopeName, pv::eventScopeValues);
148     if (s == pv::eventScopeValues.end())
149     {
150         lg2::error("Invalid event scope used in registry: {SCOPE}", "SCOPE",
151                    eventScopeName);
152 
153         throw std::runtime_error(
154             "Invalid event scope used in message registry");
155     }
156     return std::get<pv::fieldValuePos>(*s);
157 }
158 
getSRCReasonCode(const nlohmann::json & src,const std::string & name)159 uint16_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         lg2::error(
166             "Invalid reason code {RC} in message registry, error name = {ERROR}",
167             "RC", rc, "ERROR", name);
168 
169         throw std::runtime_error("Invalid reason code in message registry");
170     }
171     return reasonCode;
172 }
173 
getSRCType(const nlohmann::json & src,const std::string & name)174 uint8_t getSRCType(const nlohmann::json& src, const std::string& name)
175 {
176     // Looks like: "22"
177     std::string srcType = src["Type"];
178     size_t type = strtoul(srcType.c_str(), nullptr, 16);
179     if ((type == 0) || (srcType.size() != 2)) // 1 hex byte
180     {
181         lg2::error(
182             "Invalid SRC Type {TYPE} in message registry, error name = {ERROR}",
183             "TYPE", srcType, "ERROR", name);
184 
185         throw std::runtime_error("Invalid SRC Type in message registry");
186     }
187 
188     return type;
189 }
190 
getSRCDeconfigFlag(const nlohmann::json & src)191 bool getSRCDeconfigFlag(const nlohmann::json& src)
192 {
193     return src["DeconfigFlag"].get<bool>();
194 }
195 
getSRCCheckstopFlag(const nlohmann::json & src)196 bool getSRCCheckstopFlag(const nlohmann::json& src)
197 {
198     return src["CheckstopFlag"].get<bool>();
199 }
200 
201 std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
getSRCHexwordFields(const nlohmann::json & src,const std::string & name)202     getSRCHexwordFields(const nlohmann::json& src, const std::string& name)
203 {
204     std::map<SRC::WordNum, SRC::AdditionalDataField> hexwordFields;
205 
206     // Build the map of which AdditionalData fields to use for which SRC words
207 
208     // Like:
209     // {
210     //   "8":
211     //   {
212     //     "AdditionalDataPropSource": "TEST"
213     //   }
214     //
215     // }
216 
217     for (const auto& word : src["Words6To9"].items())
218     {
219         std::string num = word.key();
220         size_t wordNum = std::strtoul(num.c_str(), nullptr, 10);
221 
222         if (wordNum == 0)
223         {
224             lg2::error(
225                 "Invalid SRC word number {NUM} in message registry, error name = {ERROR}",
226                 "NUM", num, "ERROR", name);
227 
228             throw std::runtime_error("Invalid SRC word in message registry");
229         }
230 
231         auto attributes = word.value();
232 
233         // Use an empty string for the description if it does not exist.
234         auto itr = attributes.find("Description");
235         std::string desc = (attributes.end() != itr) ? *itr : "";
236 
237         std::tuple<std::string, std::string> adPropSourceDesc(
238             attributes["AdditionalDataPropSource"], desc);
239         hexwordFields[wordNum] = std::move(adPropSourceDesc);
240     }
241 
242     if (!hexwordFields.empty())
243     {
244         return hexwordFields;
245     }
246 
247     return std::nullopt;
248 }
getSRCSymptomIDFields(const nlohmann::json & src,const std::string & name)249 std::optional<std::vector<SRC::WordNum>> getSRCSymptomIDFields(
250     const nlohmann::json& src, const std::string& name)
251 {
252     std::vector<SRC::WordNum> symptomIDFields;
253 
254     // Looks like:
255     // "SymptomIDFields": ["SRCWord3", "SRCWord6"],
256 
257     for (const std::string field : src["SymptomIDFields"])
258     {
259         // Just need the last digit off the end, e.g. SRCWord6.
260         // The schema enforces the format of these.
261         auto srcWordNum = field.substr(field.size() - 1);
262         size_t num = std::strtoul(srcWordNum.c_str(), nullptr, 10);
263         if (num == 0)
264         {
265             lg2::error(
266                 "Invalid symptom ID field {FIELD} in message registry, error name = {ERROR}",
267                 "FIELD", field, "ERROR", name);
268 
269             throw std::runtime_error("Invalid symptom ID in message registry");
270         }
271         symptomIDFields.push_back(num);
272     }
273     if (!symptomIDFields.empty())
274     {
275         return symptomIDFields;
276     }
277 
278     return std::nullopt;
279 }
280 
getComponentID(uint8_t srcType,uint16_t reasonCode,const nlohmann::json & pelEntry,const std::string & name)281 uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode,
282                         const nlohmann::json& pelEntry, const std::string& name)
283 {
284     uint16_t id = 0;
285 
286     // If the ComponentID field is there, use that.  Otherwise, if it's a
287     // 0xBD BMC error SRC, use the reasoncode.
288     if (pelEntry.contains("ComponentID"))
289     {
290         std::string componentID = pelEntry["ComponentID"];
291         id = strtoul(componentID.c_str(), nullptr, 16);
292     }
293     else
294     {
295         // On BMC error SRCs (BD), can just get the component ID from
296         // the first byte of the reason code.
297         if (srcType == static_cast<uint8_t>(SRCType::bmcError))
298         {
299             id = reasonCode & 0xFF00;
300         }
301         else
302         {
303             lg2::error(
304                 "Missing component ID field in message registry, error name = {ERROR}",
305                 "ERROR", name);
306 
307             throw std::runtime_error(
308                 "Missing component ID field in message registry");
309         }
310     }
311 
312     return id;
313 }
314 
315 /**
316  * @brief Says if the JSON is the format that contains AdditionalData keys
317  *        as in index into them.
318  *
319  * @param[in] json - The highest level callout JSON
320  *
321  * @return bool - If it is the AdditionalData format or not
322  */
calloutUsesAdditionalData(const nlohmann::json & json)323 bool calloutUsesAdditionalData(const nlohmann::json& json)
324 {
325     return (json.contains("ADName") &&
326             json.contains("CalloutsWithTheirADValues"));
327 }
328 
329 /**
330  * @brief Finds the callouts to use when there is no AdditionalData,
331  *        but the system type may be used as a key.
332  *
333  * A sample calloutList array looks like the following.  The System and Systems
334  * key are optional.
335  *
336  * System key - Value of the key will be the system name as a string. The
337  * callouts for a specific system can define under this key.
338  *
339  * Systems key - Value of the key will be an array of system names in the form
340  * of string. The callouts common to the systems mentioned in the array can
341  * define under this key.
342  *
343  * If both System and Systems not present it means that entry applies to every
344  * configuration that doesn't have another entry with a matching System and
345  * Systems key.
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  *    },
361  *    {
362  *        "Systems": ["system1", 'system2"],
363  *        "CalloutList":
364  *        [
365  *            {
366  *                "Priority": "high",
367  *                "LocCode": "P0-C1"
368  *            },
369  *            {
370  *                "Priority": "low",
371  *                "LocCode": "P0"
372  *            }
373  *        ]
374  *    }
375  *
376  * @param[in] json - The callout JSON
377  * @param[in] systemNames - List of compatible system type names
378  * @param[out] calloutLists - The JSON array which will hold the calloutlist to
379  * use specific to the system.
380  *
381  * @return - Throws runtime exception if json is not an array or if calloutLists
382  *           is empty.
383  */
findCalloutList(const nlohmann::json & json,const std::vector<std::string> & systemNames,nlohmann::json & calloutLists)384 static void findCalloutList(const nlohmann::json& json,
385                             const std::vector<std::string>& systemNames,
386                             nlohmann::json& calloutLists)
387 {
388     if (!json.is_array())
389     {
390         throw std::runtime_error{"findCalloutList was not passed a JSON array"};
391     }
392 
393     // Flag to indicate whether system specific callouts found or not
394     bool foundCallouts = false;
395 
396     for (const auto& callouts : json)
397     {
398         if (callouts.contains("System"))
399         {
400             if (std::ranges::find(systemNames,
401                                   callouts["System"].get<std::string>()) !=
402                 systemNames.end())
403             {
404                 calloutLists.insert(calloutLists.end(),
405                                     callouts["CalloutList"].begin(),
406                                     callouts["CalloutList"].end());
407                 foundCallouts = true;
408             }
409             continue;
410         }
411 
412         if (callouts.contains("Systems"))
413         {
414             std::vector<std::string> systems =
415                 callouts["Systems"].get<std::vector<std::string>>();
416             auto inSystemNames = [systemNames](const auto& system) {
417                 return (std::ranges::find(systemNames, system) !=
418                         systemNames.end());
419             };
420             if (std::ranges::any_of(systems, inSystemNames))
421             {
422                 calloutLists.insert(calloutLists.end(),
423                                     callouts["CalloutList"].begin(),
424                                     callouts["CalloutList"].end());
425                 foundCallouts = true;
426             }
427             continue;
428         }
429 
430         // Any entry if neither System/Systems key matches with system name
431         if (!foundCallouts)
432         {
433             calloutLists.insert(calloutLists.end(),
434                                 callouts["CalloutList"].begin(),
435                                 callouts["CalloutList"].end());
436         }
437     }
438     if (calloutLists.empty())
439     {
440         std::string types;
441         std::for_each(systemNames.begin(), systemNames.end(),
442                       [&types](const auto& t) { types += t + '|'; });
443         lg2::warning(
444             "No matching system name entry or default system name entry "
445             " for PEL callout list, names = {TYPES}",
446             "TYPES", types);
447 
448         throw std::runtime_error{
449             "Could not find a CalloutList JSON for this error and system name"};
450     }
451 }
452 
453 /**
454  * @brief Creates a RegistryCallout based on the input JSON.
455  *
456  * The JSON looks like:
457  *     {
458  *          "Priority": "high",
459  *          "LocCode": "E1"
460  *          ...
461  *     }
462  *
463  * Schema validation enforces what keys are present.
464  *
465  * @param[in] json - The JSON dictionary entry for a callout
466  *
467  * @return RegistryCallout - A filled in RegistryCallout
468  */
makeRegistryCallout(const nlohmann::json & json)469 RegistryCallout makeRegistryCallout(const nlohmann::json& json)
470 {
471     RegistryCallout callout;
472 
473     callout.priority = "high";
474     callout.useInventoryLocCode = false;
475 
476     if (json.contains("Priority"))
477     {
478         callout.priority = json["Priority"].get<std::string>();
479     }
480 
481     if (json.contains("LocCode"))
482     {
483         callout.locCode = json["LocCode"].get<std::string>();
484     }
485 
486     if (json.contains("Procedure"))
487     {
488         callout.procedure = json["Procedure"].get<std::string>();
489     }
490     else if (json.contains("SymbolicFRU"))
491     {
492         callout.symbolicFRU = json["SymbolicFRU"].get<std::string>();
493     }
494     else if (json.contains("SymbolicFRUTrusted"))
495     {
496         callout.symbolicFRUTrusted =
497             json["SymbolicFRUTrusted"].get<std::string>();
498     }
499 
500     if (json.contains("UseInventoryLocCode"))
501     {
502         callout.useInventoryLocCode = json["UseInventoryLocCode"].get<bool>();
503     }
504 
505     return callout;
506 }
507 
508 /**
509  * @brief Returns the callouts to use when an AdditionalData key is
510  *        required to find the correct entries.
511  *
512  *       The System property is used to find which CalloutList to use.
513  *       If System is missing, then that CalloutList is valid for
514  *       everything.
515  *
516  * The JSON looks like:
517  *    {
518  *        "System": "system1",
519  *        "CalloutList":
520  *        [
521  *            {
522  *                "Priority": "high",
523  *                "LocCode": "P1-C1"
524  *            },
525  *            {
526  *                "Priority": "low",
527  *                "LocCode": "P1"
528  *            }
529  *        ]
530  *    },
531  *    {
532  *        "Systems": ["system1", 'system2"],
533  *        "CalloutList":
534  *        [
535  *            {
536  *                "Priority": "high",
537  *                "LocCode": "P0-C1"
538  *            },
539  *            {
540  *                "Priority": "low",
541  *                "LocCode": "P0"
542  *            }
543  *        ]
544  *    }
545  *
546  * @param[in] json - The callout JSON
547  * @param[in] systemNames - List of compatible system type names
548  *
549  * @return std::vector<RegistryCallout> - The callouts to use
550  */
getCalloutsWithoutAD(const nlohmann::json & json,const std::vector<std::string> & systemNames)551 std::vector<RegistryCallout> getCalloutsWithoutAD(
552     const nlohmann::json& json, const std::vector<std::string>& systemNames)
553 {
554     std::vector<RegistryCallout> calloutEntries;
555 
556     nlohmann::json calloutLists = nlohmann::json::array();
557 
558     // Find the CalloutList to use based on the system type
559     findCalloutList(json, systemNames, calloutLists);
560 
561     // We finally found the callouts, make the objects.
562     for (const auto& callout : calloutLists)
563     {
564         calloutEntries.push_back(makeRegistryCallout(callout));
565     }
566 
567     return calloutEntries;
568 }
569 
570 /**
571  * @brief Returns the callouts to use when an AdditionalData key is
572  *        required to find the correct entries.
573  *
574  * The JSON looks like:
575  *    {
576  *        "ADName": "PROC_NUM",
577  *        "CalloutsWithTheirADValues":
578  *        [
579  *            {
580  *                "ADValue": "0",
581  *                "Callouts":
582  *                [
583  *                    {
584  *                        "CalloutList":
585  *                        [
586  *                            {
587  *                                "Priority": "high",
588  *                                "LocCode": "P1-C5"
589  *                            }
590  *                        ]
591  *                    }
592  *                ]
593  *            }
594  *        ]
595  *     }
596  *
597  * Note that the "Callouts" entry above is the same as the top level
598  * entry used when there is no AdditionalData key.
599  *
600  * @param[in] json - The callout JSON
601  * @param[in] systemNames - List of compatible system type names
602  * @param[in] additionalData - The AdditionalData property
603  *
604  * @return std::vector<RegistryCallout> - The callouts to use
605  */
getCalloutsUsingAD(const nlohmann::json & json,const std::vector<std::string> & systemNames,const AdditionalData & additionalData)606 std::vector<RegistryCallout> getCalloutsUsingAD(
607     const nlohmann::json& json, const std::vector<std::string>& systemNames,
608     const AdditionalData& additionalData)
609 {
610     // This indicates which AD field we'll be using
611     auto keyName = json["ADName"].get<std::string>();
612 
613     // Get the actual value from the AD data
614     auto adValue = additionalData.getValue(keyName);
615 
616     if (!adValue)
617     {
618         // The AdditionalData did not contain the necessary key
619         lg2::warning("The PEL message registry callouts JSON "
620                      "said to use an AdditionalData key that isn't in the "
621                      "AdditionalData event log property, key = {KEY}",
622                      "KEY", keyName);
623         throw std::runtime_error{
624             "Missing AdditionalData entry for this callout"};
625     }
626 
627     const auto& callouts = json["CalloutsWithTheirADValues"];
628 
629     // find the entry with that AD value
630     auto it = std::find_if(
631         callouts.begin(), callouts.end(), [adValue](const nlohmann::json& j) {
632             return *adValue == j["ADValue"].get<std::string>();
633         });
634 
635     if (it == callouts.end())
636     {
637         // This can happen if not all possible values were in the
638         // message registry and that's fine.  There may be a
639         // "CalloutsWhenNoADMatch" section that contains callouts
640         // to use in this case.
641         if (json.contains("CalloutsWhenNoADMatch"))
642         {
643             return getCalloutsWithoutAD(json["CalloutsWhenNoADMatch"],
644                                         systemNames);
645         }
646         return std::vector<RegistryCallout>{};
647     }
648 
649     // Proceed to find the callouts possibly based on system type.
650     return getCalloutsWithoutAD((*it)["Callouts"], systemNames);
651 }
652 
653 /**
654  * @brief Returns the journal capture information
655  *
656  *  The JSON looks like:
657  *    "JournalCapture": {
658  *        "NumLines": 30
659  *    }
660  *
661  *    "JournalCapture":
662  *    {
663  *        "Sections": [
664  *            {
665  *                "SyslogID": "phosphor-log-manager",
666  *                "NumLines": 20
667  *            }
668  *        ]
669  *    }
670  *
671  * @param json - The journal capture JSON
672  * @return JournalCapture - The filled in variant
673  */
getJournalCapture(const nlohmann::json & json)674 JournalCapture getJournalCapture(const nlohmann::json& json)
675 {
676     JournalCapture capt;
677 
678     // Primary key is either NumLines or Sections.
679     if (json.contains("NumLines"))
680     {
681         capt = json.at("NumLines").get<size_t>();
682     }
683     else if (json.contains("Sections"))
684     {
685         AppCaptureList captures;
686         for (const auto& capture : json.at("Sections"))
687         {
688             AppCapture ac;
689             ac.syslogID = capture.at("SyslogID").get<std::string>();
690             ac.numLines = capture.at("NumLines").get<size_t>();
691             captures.push_back(std::move(ac));
692         }
693 
694         capt = captures;
695     }
696     else
697     {
698         lg2::error("JournalCapture section not the right format");
699         throw std::runtime_error{"JournalCapture section not the right format"};
700     }
701 
702     return capt;
703 }
704 
705 } // namespace helper
706 
lookup(const std::string & name,LookupType type,bool toCache)707 std::optional<Entry> Registry::lookup(const std::string& name, LookupType type,
708                                       bool toCache)
709 {
710     std::optional<nlohmann::json> registryTmp;
711     auto& registryOpt = (_registry) ? _registry : registryTmp;
712     if (!registryOpt)
713     {
714         registryOpt = readRegistry(_registryFile);
715         if (!registryOpt)
716         {
717             return std::nullopt;
718         }
719         else if (toCache)
720         {
721             // Save message registry in memory for peltool
722             _registry = std::move(registryTmp);
723         }
724     }
725     auto& reg = (_registry) ? _registry : registryTmp;
726     const auto& registry = reg.value();
727     // Find an entry with this name in the PEL array.
728     auto e = std::find_if(
729         registry["PELs"].begin(), registry["PELs"].end(),
730         [&name, &type](const nlohmann::json& j) {
731             return ((name == j.at("Name").get<std::string>() &&
732                      type == LookupType::name) ||
733                     (name == j.at("SRC").at("ReasonCode").get<std::string>() &&
734                      type == LookupType::reasonCode));
735         });
736 
737     if (e != registry["PELs"].end())
738     {
739         // Fill in the Entry structure from the JSON.  Most, but not all, fields
740         // are optional.
741 
742         try
743         {
744             Entry entry;
745             entry.name = (*e)["Name"];
746 
747             if (e->contains("Subsystem"))
748             {
749                 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]);
750             }
751 
752             if (e->contains("ActionFlags"))
753             {
754                 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]);
755             }
756 
757             if (e->contains("MfgActionFlags"))
758             {
759                 entry.mfgActionFlags =
760                     helper::getActionFlags((*e)["MfgActionFlags"]);
761             }
762 
763             if (e->contains("Severity"))
764             {
765                 entry.severity = helper::getSeverities((*e)["Severity"]);
766             }
767 
768             if (e->contains("MfgSeverity"))
769             {
770                 entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]);
771             }
772 
773             if (e->contains("EventType"))
774             {
775                 entry.eventType = helper::getEventType((*e)["EventType"]);
776             }
777 
778             if (e->contains("EventScope"))
779             {
780                 entry.eventScope = helper::getEventScope((*e)["EventScope"]);
781             }
782 
783             auto& src = (*e)["SRC"];
784             entry.src.reasonCode = helper::getSRCReasonCode(src, name);
785 
786             if (src.contains("Type"))
787             {
788                 entry.src.type = helper::getSRCType(src, name);
789             }
790             else
791             {
792                 entry.src.type = static_cast<uint8_t>(SRCType::bmcError);
793             }
794 
795             // Now that we know the SRC type and reason code,
796             // we can get the component ID.
797             entry.componentID = helper::getComponentID(
798                 entry.src.type, entry.src.reasonCode, *e, name);
799 
800             if (src.contains("Words6To9"))
801             {
802                 entry.src.hexwordADFields =
803                     helper::getSRCHexwordFields(src, name);
804             }
805 
806             if (src.contains("SymptomIDFields"))
807             {
808                 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
809             }
810 
811             if (src.contains("DeconfigFlag"))
812             {
813                 entry.src.deconfigFlag = helper::getSRCDeconfigFlag(src);
814             }
815 
816             if (src.contains("CheckstopFlag"))
817             {
818                 entry.src.checkstopFlag = helper::getSRCCheckstopFlag(src);
819             }
820 
821             auto& doc = (*e)["Documentation"];
822             entry.doc.message = doc["Message"];
823             entry.doc.description = doc["Description"];
824             if (doc.contains("MessageArgSources"))
825             {
826                 entry.doc.messageArgSources = doc["MessageArgSources"];
827             }
828 
829             // If there are callouts defined, save the JSON for later
830             if (_loadCallouts)
831             {
832                 if (e->contains("Callouts"))
833                 {
834                     entry.callouts = (*e)["Callouts"];
835                 }
836                 else if (e->contains("CalloutsUsingAD"))
837                 {
838                     entry.callouts = (*e)["CalloutsUsingAD"];
839                 }
840             }
841 
842             if (e->contains("JournalCapture"))
843             {
844                 entry.journalCapture =
845                     helper::getJournalCapture((*e)["JournalCapture"]);
846             }
847 
848             return entry;
849         }
850         catch (const std::exception& ex)
851         {
852             lg2::error("Found invalid message registry field. Error: {ERROR}",
853                        "ERROR", ex);
854         }
855     }
856 
857     return std::nullopt;
858 }
859 
readRegistry(const std::filesystem::path & registryFile)860 std::optional<nlohmann::json> Registry::readRegistry(
861     const std::filesystem::path& registryFile)
862 {
863     // Look in /etc first in case someone put a test file there
864     fs::path debugFile{fs::path{debugFilePath} / registryFileName};
865     nlohmann::json registry;
866     std::ifstream file;
867 
868     if (fs::exists(debugFile))
869     {
870         lg2::info("Using debug PEL message registry");
871         file.open(debugFile);
872     }
873     else
874     {
875         file.open(registryFile);
876     }
877 
878     try
879     {
880         registry = nlohmann::json::parse(file);
881     }
882     catch (const std::exception& e)
883     {
884         lg2::error("Error parsing message registry JSON. Error: {ERROR}",
885                    "ERROR", e);
886         return std::nullopt;
887     }
888     return registry;
889 }
890 
getCallouts(const nlohmann::json & calloutJSON,const std::vector<std::string> & systemNames,const AdditionalData & additionalData)891 std::vector<RegistryCallout> Registry::getCallouts(
892     const nlohmann::json& calloutJSON,
893     const std::vector<std::string>& systemNames,
894     const AdditionalData& additionalData)
895 {
896     // The JSON may either use an AdditionalData key
897     // as an index, or not.
898     if (helper::calloutUsesAdditionalData(calloutJSON))
899     {
900         return helper::getCalloutsUsingAD(calloutJSON, systemNames,
901                                           additionalData);
902     }
903 
904     return helper::getCalloutsWithoutAD(calloutJSON, systemNames);
905 }
906 
907 } // namespace message
908 } // namespace pels
909 } // namespace openpower
910