blob: c8c37f91d29f79e1eb46135cdbf429cd764953fd [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
22#include <fstream>
23#include <phosphor-logging/log.hpp>
24
25namespace openpower
26{
27namespace pels
28{
29namespace message
30{
31
32namespace pv = pel_values;
33namespace fs = std::filesystem;
34using namespace phosphor::logging;
35
36constexpr auto debugFilePath = "/etc/phosphor-logging/";
37
38namespace helper
39{
40
41uint8_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 log<level::ERR>("Invalid subsystem name used in message registry",
49 entry("SUBSYSTEM=%s", subsystemName.c_str()));
50
51 throw std::runtime_error("Invalid subsystem used in message registry");
52 }
53
54 return std::get<pv::fieldValuePos>(*ss);
55}
56
57uint8_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 log<level::ERR>("Invalid severity name used in message registry",
64 entry("SEVERITY=%s", severityName.c_str()));
65
66 throw std::runtime_error("Invalid severity used in message registry");
67 }
68
69 return std::get<pv::fieldValuePos>(*s);
70}
71
Matt Spinleraadccc82020-04-10 14:33:42 -050072std::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
Matt Spinler367144c2019-09-19 15:33:52 -0500107uint16_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 log<level::ERR>("Invalid action flag name used in message registry",
119 entry("FLAG=%s", flag.c_str()));
120
121 throw std::runtime_error(
122 "Invalid action flag used in message registry");
123 }
124
125 actionFlags |= std::get<pv::fieldValuePos>(*s);
126 }
127
128 return actionFlags;
129}
130
131uint8_t getEventType(const std::string& eventTypeName)
132{
133 auto t = pv::findByName(eventTypeName, pv::eventTypeValues);
134 if (t == pv::eventTypeValues.end())
135 {
136 log<level::ERR>("Invalid event type used in message registry",
137 entry("EVENT_TYPE=%s", eventTypeName.c_str()));
138
139 throw std::runtime_error("Invalid event type used in message registry");
140 }
141 return std::get<pv::fieldValuePos>(*t);
142}
143
144uint8_t getEventScope(const std::string& eventScopeName)
145{
146 auto s = pv::findByName(eventScopeName, pv::eventScopeValues);
147 if (s == pv::eventScopeValues.end())
148 {
149 log<level::ERR>("Invalid event scope used in registry",
150 entry("EVENT_SCOPE=%s", eventScopeName.c_str()));
151
152 throw std::runtime_error(
153 "Invalid event scope used in message registry");
154 }
155 return std::get<pv::fieldValuePos>(*s);
156}
157
Matt Spinler93e29322019-09-20 11:16:15 -0500158uint16_t getSRCReasonCode(const nlohmann::json& src, const std::string& name)
159{
160 std::string rc = src["ReasonCode"];
161 uint16_t reasonCode = strtoul(rc.c_str(), nullptr, 16);
162 if (reasonCode == 0)
163 {
164 log<phosphor::logging::level::ERR>(
165 "Invalid reason code in message registry",
166 entry("ERROR_NAME=%s", name.c_str()),
167 entry("REASON_CODE=%s", rc.c_str()));
168
169 throw std::runtime_error("Invalid reason code in message registry");
170 }
171 return reasonCode;
172}
173
174uint8_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 log<phosphor::logging::level::ERR>(
182 "Invalid SRC Type in message registry",
183 entry("ERROR_NAME=%s", name.c_str()),
184 entry("SRC_TYPE=%s", srcType.c_str()));
185
186 throw std::runtime_error("Invalid SRC Type in message registry");
187 }
188
189 return type;
190}
191
192std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
193 getSRCHexwordFields(const nlohmann::json& src, const std::string& name)
194{
195 std::map<SRC::WordNum, SRC::AdditionalDataField> hexwordFields;
196
197 // Build the map of which AdditionalData fields to use for which SRC words
198
199 // Like:
200 // {
201 // "8":
202 // {
203 // "AdditionalDataPropSource": "TEST"
204 // }
205 //
206 // }
207
208 for (const auto& word : src["Words6To9"].items())
209 {
210 std::string num = word.key();
211 size_t wordNum = std::strtoul(num.c_str(), nullptr, 10);
212
213 if (wordNum == 0)
214 {
215 log<phosphor::logging::level::ERR>(
216 "Invalid SRC word number in message registry",
217 entry("ERROR_NAME=%s", name.c_str()),
218 entry("SRC_WORD_NUM=%s", num.c_str()));
219
220 throw std::runtime_error("Invalid SRC word in message registry");
221 }
222
223 auto attributes = word.value();
Zane Shelleye8db29b2021-11-13 10:34:07 -0600224
225 // Use an empty string for the description if it does not exist.
226 auto itr = attributes.find("Description");
227 std::string desc = (attributes.end() != itr) ? *itr : "";
228
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800229 std::tuple<std::string, std::string> adPropSourceDesc(
Zane Shelleye8db29b2021-11-13 10:34:07 -0600230 attributes["AdditionalDataPropSource"], desc);
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800231 hexwordFields[wordNum] = std::move(adPropSourceDesc);
Matt Spinler93e29322019-09-20 11:16:15 -0500232 }
233
234 if (!hexwordFields.empty())
235 {
236 return hexwordFields;
237 }
238
239 return std::nullopt;
240}
241std::optional<std::vector<SRC::WordNum>>
242 getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name)
243{
244 std::vector<SRC::WordNum> symptomIDFields;
245
246 // Looks like:
247 // "SymptomIDFields": ["SRCWord3", "SRCWord6"],
248
Matt Spinler9d59d582021-05-19 07:57:10 -0600249 for (const std::string field : src["SymptomIDFields"])
Matt Spinler93e29322019-09-20 11:16:15 -0500250 {
251 // Just need the last digit off the end, e.g. SRCWord6.
252 // The schema enforces the format of these.
253 auto srcWordNum = field.substr(field.size() - 1);
254 size_t num = std::strtoul(srcWordNum.c_str(), nullptr, 10);
255 if (num == 0)
256 {
257 log<phosphor::logging::level::ERR>(
258 "Invalid symptom ID field in message registry",
259 entry("ERROR_NAME=%s", name.c_str()),
260 entry("FIELD_NAME=%s", srcWordNum.c_str()));
261
262 throw std::runtime_error("Invalid symptom ID in message registry");
263 }
264 symptomIDFields.push_back(num);
265 }
266 if (!symptomIDFields.empty())
267 {
268 return symptomIDFields;
269 }
270
271 return std::nullopt;
272}
273
274uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode,
275 const nlohmann::json& pelEntry, const std::string& name)
276{
277 uint16_t id = 0;
278
279 // If the ComponentID field is there, use that. Otherwise, if it's a
280 // 0xBD BMC error SRC, use the reasoncode.
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500281 if (pelEntry.contains("ComponentID"))
Matt Spinler93e29322019-09-20 11:16:15 -0500282 {
283 std::string componentID = pelEntry["ComponentID"];
284 id = strtoul(componentID.c_str(), nullptr, 16);
285 }
286 else
287 {
288 // On BMC error SRCs (BD), can just get the component ID from
289 // the first byte of the reason code.
290 if (srcType == static_cast<uint8_t>(SRCType::bmcError))
291 {
292 id = reasonCode & 0xFF00;
293 }
294 else
295 {
296 log<level::ERR>("Missing component ID field in message registry",
297 entry("ERROR_NAME=%s", name.c_str()));
298
299 throw std::runtime_error(
300 "Missing component ID field in message registry");
301 }
302 }
303
304 return id;
305}
306
Matt Spinler6b427cc2020-04-09 09:42:59 -0500307/**
308 * @brief Says if the JSON is the format that contains AdditionalData keys
309 * as in index into them.
310 *
311 * @param[in] json - The highest level callout JSON
312 *
313 * @return bool - If it is the AdditionalData format or not
314 */
315bool calloutUsesAdditionalData(const nlohmann::json& json)
316{
317 return (json.contains("ADName") &&
318 json.contains("CalloutsWithTheirADValues"));
319}
320
321/**
322 * @brief Finds the callouts to use when there is no AdditionalData,
323 * but the system type may be used as a key.
324 *
325 * One entry in the array looks like the following. The System key
326 * is optional and if not present it means that entry applies to
327 * every configuration that doesn't have another entry with a matching
328 * System key.
329 *
330 * {
331 * "System": "system1",
332 * "CalloutList":
333 * [
334 * {
335 * "Priority": "high",
336 * "LocCode": "P1-C1"
337 * },
338 * {
339 * "Priority": "low",
340 * "LocCode": "P1"
341 * }
342 * ]
343 * }
344 */
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500345const nlohmann::json&
346 findCalloutList(const nlohmann::json& json,
347 const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500348{
349 const nlohmann::json* callouts = nullptr;
350
351 if (!json.is_array())
352 {
353 throw std::runtime_error{"findCalloutList was not passed a JSON array"};
354 }
355
356 // The entry with the system type match will take precedence over the entry
357 // without any "System" field in it at all, which will match all other
358 // cases.
359 for (const auto& calloutList : json)
360 {
361 if (calloutList.contains("System"))
362 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500363 if (std::find(systemNames.begin(), systemNames.end(),
364 calloutList["System"].get<std::string>()) !=
365 systemNames.end())
Matt Spinler6b427cc2020-04-09 09:42:59 -0500366 {
367 callouts = &calloutList["CalloutList"];
368 break;
369 }
370 }
371 else
372 {
373 // Any entry with no System key
374 callouts = &calloutList["CalloutList"];
375 }
376 }
377
378 if (!callouts)
379 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500380 std::string types;
381 std::for_each(systemNames.begin(), systemNames.end(),
382 [&types](const auto& t) { types += t + '|'; });
Matt Spinler6b427cc2020-04-09 09:42:59 -0500383 log<level::WARNING>(
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500384 "No matching system name entry or default system name entry "
Matt Spinler6b427cc2020-04-09 09:42:59 -0500385 " for PEL callout list",
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500386 entry("SYSTEMNAMES=%s", types.c_str()));
Matt Spinler6b427cc2020-04-09 09:42:59 -0500387
388 throw std::runtime_error{
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500389 "Could not find a CalloutList JSON for this error and system name"};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500390 }
391
392 return *callouts;
393}
394
395/**
396 * @brief Creates a RegistryCallout based on the input JSON.
397 *
398 * The JSON looks like:
399 * {
400 * "Priority": "high",
401 * "LocCode": "E1"
402 * ...
403 * }
404 *
405 * Schema validation enforces what keys are present.
406 *
407 * @param[in] json - The JSON dictionary entry for a callout
408 *
409 * @return RegistryCallout - A filled in RegistryCallout
410 */
411RegistryCallout makeRegistryCallout(const nlohmann::json& json)
412{
413 RegistryCallout callout;
414
415 callout.priority = "high";
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500416 callout.useInventoryLocCode = false;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500417
418 if (json.contains("Priority"))
419 {
420 callout.priority = json["Priority"].get<std::string>();
421 }
422
423 if (json.contains("LocCode"))
424 {
425 callout.locCode = json["LocCode"].get<std::string>();
426 }
427
428 if (json.contains("Procedure"))
429 {
430 callout.procedure = json["Procedure"].get<std::string>();
431 }
432 else if (json.contains("SymbolicFRU"))
433 {
434 callout.symbolicFRU = json["SymbolicFRU"].get<std::string>();
435 }
436 else if (json.contains("SymbolicFRUTrusted"))
437 {
438 callout.symbolicFRUTrusted =
439 json["SymbolicFRUTrusted"].get<std::string>();
440 }
441
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500442 if (json.contains("UseInventoryLocCode"))
443 {
444 callout.useInventoryLocCode = json["UseInventoryLocCode"].get<bool>();
445 }
446
Matt Spinler6b427cc2020-04-09 09:42:59 -0500447 return callout;
448}
449
450/**
451 * @brief Returns the callouts to use when an AdditionalData key is
452 * required to find the correct entries.
453 *
454 * The System property is used to find which CalloutList to use.
455 * If System is missing, then that CalloutList is valid for
456 * everything.
457 *
458 * The JSON looks like:
459 * [
460 * {
461 * "System": "systemA",
462 * "CalloutList":
463 * [
464 * {
465 * "Priority": "high",
466 * "LocCode": "P1-C5"
467 * }
468 * ]
469 * }
470 * ]
471 *
472 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500473 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500474 *
475 * @return std::vector<RegistryCallout> - The callouts to use
476 */
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500477std::vector<RegistryCallout>
478 getCalloutsWithoutAD(const nlohmann::json& json,
479 const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500480{
481 std::vector<RegistryCallout> calloutEntries;
482
483 // Find the CalloutList to use based on the system type
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500484 const auto& calloutList = findCalloutList(json, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500485
486 // We finally found the callouts, make the objects.
487 for (const auto& callout : calloutList)
488 {
489 calloutEntries.push_back(std::move(makeRegistryCallout(callout)));
490 }
491
492 return calloutEntries;
493}
494
495/**
496 * @brief Returns the callouts to use when an AdditionalData key is
497 * required to find the correct entries.
498 *
499 * The JSON looks like:
500 * {
501 * "ADName": "PROC_NUM",
502 * "CalloutsWithTheirADValues":
503 * [
504 * {
505 * "ADValue": "0",
506 * "Callouts":
507 * [
508 * {
509 * "CalloutList":
510 * [
511 * {
512 * "Priority": "high",
513 * "LocCode": "P1-C5"
514 * }
515 * ]
516 * }
517 * ]
518 * }
519 * ]
520 * }
521 *
522 * Note that the "Callouts" entry above is the same as the top level
523 * entry used when there is no AdditionalData key.
524 *
525 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500526 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500527 * @param[in] additionalData - The AdditionalData property
528 *
529 * @return std::vector<RegistryCallout> - The callouts to use
530 */
531std::vector<RegistryCallout>
532 getCalloutsUsingAD(const nlohmann::json& json,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500533 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500534 const AdditionalData& additionalData)
535{
536 // This indicates which AD field we'll be using
537 auto keyName = json["ADName"].get<std::string>();
538
539 // Get the actual value from the AD data
540 auto adValue = additionalData.getValue(keyName);
541
542 if (!adValue)
543 {
544 // The AdditionalData did not contain the necessary key
545 log<level::WARNING>(
546 "The PEL message registry callouts JSON "
547 "said to use an AdditionalData key that isn't in the "
548 "AdditionalData event log property",
549 entry("ADNAME=%s\n", keyName.c_str()));
550 throw std::runtime_error{
551 "Missing AdditionalData entry for this callout"};
552 }
553
554 const auto& callouts = json["CalloutsWithTheirADValues"];
555
556 // find the entry with that AD value
557 auto it = std::find_if(
558 callouts.begin(), callouts.end(), [adValue](const nlohmann::json& j) {
559 return *adValue == j["ADValue"].get<std::string>();
560 });
561
562 if (it == callouts.end())
563 {
Matt Spinlerf397afc2021-01-29 11:21:44 -0600564 // This can happen if not all possible values were in the
565 // message registry and that's fine.
566 return std::vector<RegistryCallout>{};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500567 }
568
569 // Proceed to find the callouts possibly based on system type.
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500570 return getCalloutsWithoutAD((*it)["Callouts"], systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500571}
572
Matt Spinler367144c2019-09-19 15:33:52 -0500573} // namespace helper
574
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800575std::optional<Entry> Registry::lookup(const std::string& name, LookupType type,
576 bool toCache)
Matt Spinler367144c2019-09-19 15:33:52 -0500577{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800578 std::optional<nlohmann::json> registryTmp;
579 auto& registryOpt = (_registry) ? _registry : registryTmp;
580 if (!registryOpt)
Matt Spinler367144c2019-09-19 15:33:52 -0500581 {
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800582 registryOpt = readRegistry(_registryFile);
583 if (!registryOpt)
584 {
585 return std::nullopt;
586 }
587 else if (toCache)
588 {
589 // Save message registry in memory for peltool
590 _registry = std::move(registryTmp);
591 }
Matt Spinler367144c2019-09-19 15:33:52 -0500592 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800593 auto& reg = (_registry) ? _registry : registryTmp;
594 const auto& registry = reg.value();
Matt Spinler367144c2019-09-19 15:33:52 -0500595 // Find an entry with this name in the PEL array.
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800596 auto e = std::find_if(
597 registry["PELs"].begin(), registry["PELs"].end(),
598 [&name, &type](const auto& j) {
599 return ((name == j["Name"] && type == LookupType::name) ||
600 (name == j["SRC"]["ReasonCode"] &&
601 type == LookupType::reasonCode));
602 });
Matt Spinler367144c2019-09-19 15:33:52 -0500603
604 if (e != registry["PELs"].end())
605 {
606 // Fill in the Entry structure from the JSON. Most, but not all, fields
607 // are optional.
608
609 try
610 {
611 Entry entry;
612 entry.name = (*e)["Name"];
Matt Spinler23970b02022-02-25 16:34:46 -0600613
614 if (e->contains("Subsystem"))
615 {
616 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]);
617 }
Matt Spinlere07f9152019-11-01 10:48:36 -0500618
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500619 if (e->contains("ActionFlags"))
Matt Spinlere07f9152019-11-01 10:48:36 -0500620 {
621 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]);
622 }
Matt Spinler367144c2019-09-19 15:33:52 -0500623
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500624 if (e->contains("MfgActionFlags"))
Matt Spinler367144c2019-09-19 15:33:52 -0500625 {
626 entry.mfgActionFlags =
627 helper::getActionFlags((*e)["MfgActionFlags"]);
628 }
629
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500630 if (e->contains("Severity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500631 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500632 entry.severity = helper::getSeverities((*e)["Severity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500633 }
634
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500635 if (e->contains("MfgSeverity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500636 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500637 entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500638 }
639
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500640 if (e->contains("EventType"))
Matt Spinler367144c2019-09-19 15:33:52 -0500641 {
642 entry.eventType = helper::getEventType((*e)["EventType"]);
643 }
644
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500645 if (e->contains("EventScope"))
Matt Spinler367144c2019-09-19 15:33:52 -0500646 {
647 entry.eventScope = helper::getEventScope((*e)["EventScope"]);
648 }
649
Matt Spinler93e29322019-09-20 11:16:15 -0500650 auto& src = (*e)["SRC"];
651 entry.src.reasonCode = helper::getSRCReasonCode(src, name);
652
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500653 if (src.contains("Type"))
Matt Spinler93e29322019-09-20 11:16:15 -0500654 {
655 entry.src.type = helper::getSRCType(src, name);
656 }
657 else
658 {
659 entry.src.type = static_cast<uint8_t>(SRCType::bmcError);
660 }
661
662 // Now that we know the SRC type and reason code,
663 // we can get the component ID.
664 entry.componentID = helper::getComponentID(
665 entry.src.type, entry.src.reasonCode, *e, name);
666
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500667 if (src.contains("Words6To9"))
Matt Spinler93e29322019-09-20 11:16:15 -0500668 {
669 entry.src.hexwordADFields =
670 helper::getSRCHexwordFields(src, name);
671 }
672
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500673 if (src.contains("SymptomIDFields"))
Matt Spinler93e29322019-09-20 11:16:15 -0500674 {
675 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
676 }
677
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800678 auto& doc = (*e)["Documentation"];
679 entry.doc.message = doc["Message"];
680 entry.doc.description = doc["Description"];
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500681 if (doc.contains("MessageArgSources"))
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800682 {
683 entry.doc.messageArgSources = doc["MessageArgSources"];
684 }
685
Matt Spinlerd8e29002020-04-09 09:11:22 -0500686 // If there are callouts defined, save the JSON for later
687 if (_loadCallouts)
688 {
689 if (e->contains("Callouts"))
690 {
691 entry.callouts = (*e)["Callouts"];
692 }
693 else if (e->contains("CalloutsUsingAD"))
694 {
695 entry.callouts = (*e)["CalloutsUsingAD"];
696 }
697 }
698
Matt Spinler367144c2019-09-19 15:33:52 -0500699 return entry;
700 }
Patrick Williams66491c62021-10-06 12:23:37 -0500701 catch (const std::exception& e)
Matt Spinler367144c2019-09-19 15:33:52 -0500702 {
703 log<level::ERR>("Found invalid message registry field",
704 entry("ERROR=%s", e.what()));
705 }
706 }
707
708 return std::nullopt;
709}
710
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800711std::optional<nlohmann::json>
712 Registry::readRegistry(const std::filesystem::path& registryFile)
713{
714 // Look in /etc first in case someone put a test file there
715 fs::path debugFile{fs::path{debugFilePath} / registryFileName};
716 nlohmann::json registry;
717 std::ifstream file;
718
719 if (fs::exists(debugFile))
720 {
721 log<level::INFO>("Using debug PEL message registry");
722 file.open(debugFile);
723 }
724 else
725 {
726 file.open(registryFile);
727 }
728
729 try
730 {
731 registry = nlohmann::json::parse(file);
732 }
Patrick Williams66491c62021-10-06 12:23:37 -0500733 catch (const std::exception& e)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800734 {
735 log<level::ERR>("Error parsing message registry JSON",
736 entry("JSON_ERROR=%s", e.what()));
737 return std::nullopt;
738 }
739 return registry;
740}
741
Matt Spinler6b427cc2020-04-09 09:42:59 -0500742std::vector<RegistryCallout>
743 Registry::getCallouts(const nlohmann::json& calloutJSON,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500744 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500745 const AdditionalData& additionalData)
746{
747 // The JSON may either use an AdditionalData key
748 // as an index, or not.
749 if (helper::calloutUsesAdditionalData(calloutJSON))
750 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500751 return helper::getCalloutsUsingAD(calloutJSON, systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500752 additionalData);
753 }
754
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500755 return helper::getCalloutsWithoutAD(calloutJSON, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500756}
757
Matt Spinler367144c2019-09-19 15:33:52 -0500758} // namespace message
759} // namespace pels
760} // namespace openpower