blob: 3f4462fc635ec133ae2176b100c13f6d5d9f1bd8 [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();
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800224 std::tuple<std::string, std::string> adPropSourceDesc(
225 attributes["AdditionalDataPropSource"], attributes["Description"]);
226 hexwordFields[wordNum] = std::move(adPropSourceDesc);
Matt Spinler93e29322019-09-20 11:16:15 -0500227 }
228
229 if (!hexwordFields.empty())
230 {
231 return hexwordFields;
232 }
233
234 return std::nullopt;
235}
236std::optional<std::vector<SRC::WordNum>>
237 getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name)
238{
239 std::vector<SRC::WordNum> symptomIDFields;
240
241 // Looks like:
242 // "SymptomIDFields": ["SRCWord3", "SRCWord6"],
243
Matt Spinler9d59d582021-05-19 07:57:10 -0600244 for (const std::string field : src["SymptomIDFields"])
Matt Spinler93e29322019-09-20 11:16:15 -0500245 {
246 // Just need the last digit off the end, e.g. SRCWord6.
247 // The schema enforces the format of these.
248 auto srcWordNum = field.substr(field.size() - 1);
249 size_t num = std::strtoul(srcWordNum.c_str(), nullptr, 10);
250 if (num == 0)
251 {
252 log<phosphor::logging::level::ERR>(
253 "Invalid symptom ID field in message registry",
254 entry("ERROR_NAME=%s", name.c_str()),
255 entry("FIELD_NAME=%s", srcWordNum.c_str()));
256
257 throw std::runtime_error("Invalid symptom ID in message registry");
258 }
259 symptomIDFields.push_back(num);
260 }
261 if (!symptomIDFields.empty())
262 {
263 return symptomIDFields;
264 }
265
266 return std::nullopt;
267}
268
269uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode,
270 const nlohmann::json& pelEntry, const std::string& name)
271{
272 uint16_t id = 0;
273
274 // If the ComponentID field is there, use that. Otherwise, if it's a
275 // 0xBD BMC error SRC, use the reasoncode.
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500276 if (pelEntry.contains("ComponentID"))
Matt Spinler93e29322019-09-20 11:16:15 -0500277 {
278 std::string componentID = pelEntry["ComponentID"];
279 id = strtoul(componentID.c_str(), nullptr, 16);
280 }
281 else
282 {
283 // On BMC error SRCs (BD), can just get the component ID from
284 // the first byte of the reason code.
285 if (srcType == static_cast<uint8_t>(SRCType::bmcError))
286 {
287 id = reasonCode & 0xFF00;
288 }
289 else
290 {
291 log<level::ERR>("Missing component ID field in message registry",
292 entry("ERROR_NAME=%s", name.c_str()));
293
294 throw std::runtime_error(
295 "Missing component ID field in message registry");
296 }
297 }
298
299 return id;
300}
301
Matt Spinler6b427cc2020-04-09 09:42:59 -0500302/**
303 * @brief Says if the JSON is the format that contains AdditionalData keys
304 * as in index into them.
305 *
306 * @param[in] json - The highest level callout JSON
307 *
308 * @return bool - If it is the AdditionalData format or not
309 */
310bool calloutUsesAdditionalData(const nlohmann::json& json)
311{
312 return (json.contains("ADName") &&
313 json.contains("CalloutsWithTheirADValues"));
314}
315
316/**
317 * @brief Finds the callouts to use when there is no AdditionalData,
318 * but the system type may be used as a key.
319 *
320 * One entry in the array looks like the following. The System key
321 * is optional and if not present it means that entry applies to
322 * every configuration that doesn't have another entry with a matching
323 * System key.
324 *
325 * {
326 * "System": "system1",
327 * "CalloutList":
328 * [
329 * {
330 * "Priority": "high",
331 * "LocCode": "P1-C1"
332 * },
333 * {
334 * "Priority": "low",
335 * "LocCode": "P1"
336 * }
337 * ]
338 * }
339 */
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500340const nlohmann::json&
341 findCalloutList(const nlohmann::json& json,
342 const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500343{
344 const nlohmann::json* callouts = nullptr;
345
346 if (!json.is_array())
347 {
348 throw std::runtime_error{"findCalloutList was not passed a JSON array"};
349 }
350
351 // The entry with the system type match will take precedence over the entry
352 // without any "System" field in it at all, which will match all other
353 // cases.
354 for (const auto& calloutList : json)
355 {
356 if (calloutList.contains("System"))
357 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500358 if (std::find(systemNames.begin(), systemNames.end(),
359 calloutList["System"].get<std::string>()) !=
360 systemNames.end())
Matt Spinler6b427cc2020-04-09 09:42:59 -0500361 {
362 callouts = &calloutList["CalloutList"];
363 break;
364 }
365 }
366 else
367 {
368 // Any entry with no System key
369 callouts = &calloutList["CalloutList"];
370 }
371 }
372
373 if (!callouts)
374 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500375 std::string types;
376 std::for_each(systemNames.begin(), systemNames.end(),
377 [&types](const auto& t) { types += t + '|'; });
Matt Spinler6b427cc2020-04-09 09:42:59 -0500378 log<level::WARNING>(
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500379 "No matching system name entry or default system name entry "
Matt Spinler6b427cc2020-04-09 09:42:59 -0500380 " for PEL callout list",
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500381 entry("SYSTEMNAMES=%s", types.c_str()));
Matt Spinler6b427cc2020-04-09 09:42:59 -0500382
383 throw std::runtime_error{
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500384 "Could not find a CalloutList JSON for this error and system name"};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500385 }
386
387 return *callouts;
388}
389
390/**
391 * @brief Creates a RegistryCallout based on the input JSON.
392 *
393 * The JSON looks like:
394 * {
395 * "Priority": "high",
396 * "LocCode": "E1"
397 * ...
398 * }
399 *
400 * Schema validation enforces what keys are present.
401 *
402 * @param[in] json - The JSON dictionary entry for a callout
403 *
404 * @return RegistryCallout - A filled in RegistryCallout
405 */
406RegistryCallout makeRegistryCallout(const nlohmann::json& json)
407{
408 RegistryCallout callout;
409
410 callout.priority = "high";
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500411 callout.useInventoryLocCode = false;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500412
413 if (json.contains("Priority"))
414 {
415 callout.priority = json["Priority"].get<std::string>();
416 }
417
418 if (json.contains("LocCode"))
419 {
420 callout.locCode = json["LocCode"].get<std::string>();
421 }
422
423 if (json.contains("Procedure"))
424 {
425 callout.procedure = json["Procedure"].get<std::string>();
426 }
427 else if (json.contains("SymbolicFRU"))
428 {
429 callout.symbolicFRU = json["SymbolicFRU"].get<std::string>();
430 }
431 else if (json.contains("SymbolicFRUTrusted"))
432 {
433 callout.symbolicFRUTrusted =
434 json["SymbolicFRUTrusted"].get<std::string>();
435 }
436
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500437 if (json.contains("UseInventoryLocCode"))
438 {
439 callout.useInventoryLocCode = json["UseInventoryLocCode"].get<bool>();
440 }
441
Matt Spinler6b427cc2020-04-09 09:42:59 -0500442 return callout;
443}
444
445/**
446 * @brief Returns the callouts to use when an AdditionalData key is
447 * required to find the correct entries.
448 *
449 * The System property is used to find which CalloutList to use.
450 * If System is missing, then that CalloutList is valid for
451 * everything.
452 *
453 * The JSON looks like:
454 * [
455 * {
456 * "System": "systemA",
457 * "CalloutList":
458 * [
459 * {
460 * "Priority": "high",
461 * "LocCode": "P1-C5"
462 * }
463 * ]
464 * }
465 * ]
466 *
467 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500468 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500469 *
470 * @return std::vector<RegistryCallout> - The callouts to use
471 */
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500472std::vector<RegistryCallout>
473 getCalloutsWithoutAD(const nlohmann::json& json,
474 const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500475{
476 std::vector<RegistryCallout> calloutEntries;
477
478 // Find the CalloutList to use based on the system type
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500479 const auto& calloutList = findCalloutList(json, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500480
481 // We finally found the callouts, make the objects.
482 for (const auto& callout : calloutList)
483 {
484 calloutEntries.push_back(std::move(makeRegistryCallout(callout)));
485 }
486
487 return calloutEntries;
488}
489
490/**
491 * @brief Returns the callouts to use when an AdditionalData key is
492 * required to find the correct entries.
493 *
494 * The JSON looks like:
495 * {
496 * "ADName": "PROC_NUM",
497 * "CalloutsWithTheirADValues":
498 * [
499 * {
500 * "ADValue": "0",
501 * "Callouts":
502 * [
503 * {
504 * "CalloutList":
505 * [
506 * {
507 * "Priority": "high",
508 * "LocCode": "P1-C5"
509 * }
510 * ]
511 * }
512 * ]
513 * }
514 * ]
515 * }
516 *
517 * Note that the "Callouts" entry above is the same as the top level
518 * entry used when there is no AdditionalData key.
519 *
520 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500521 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500522 * @param[in] additionalData - The AdditionalData property
523 *
524 * @return std::vector<RegistryCallout> - The callouts to use
525 */
526std::vector<RegistryCallout>
527 getCalloutsUsingAD(const nlohmann::json& json,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500528 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500529 const AdditionalData& additionalData)
530{
531 // This indicates which AD field we'll be using
532 auto keyName = json["ADName"].get<std::string>();
533
534 // Get the actual value from the AD data
535 auto adValue = additionalData.getValue(keyName);
536
537 if (!adValue)
538 {
539 // The AdditionalData did not contain the necessary key
540 log<level::WARNING>(
541 "The PEL message registry callouts JSON "
542 "said to use an AdditionalData key that isn't in the "
543 "AdditionalData event log property",
544 entry("ADNAME=%s\n", keyName.c_str()));
545 throw std::runtime_error{
546 "Missing AdditionalData entry for this callout"};
547 }
548
549 const auto& callouts = json["CalloutsWithTheirADValues"];
550
551 // find the entry with that AD value
552 auto it = std::find_if(
553 callouts.begin(), callouts.end(), [adValue](const nlohmann::json& j) {
554 return *adValue == j["ADValue"].get<std::string>();
555 });
556
557 if (it == callouts.end())
558 {
Matt Spinlerf397afc2021-01-29 11:21:44 -0600559 // This can happen if not all possible values were in the
560 // message registry and that's fine.
561 return std::vector<RegistryCallout>{};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500562 }
563
564 // Proceed to find the callouts possibly based on system type.
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500565 return getCalloutsWithoutAD((*it)["Callouts"], systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500566}
567
Matt Spinler367144c2019-09-19 15:33:52 -0500568} // namespace helper
569
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800570std::optional<Entry> Registry::lookup(const std::string& name, LookupType type,
571 bool toCache)
Matt Spinler367144c2019-09-19 15:33:52 -0500572{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800573 std::optional<nlohmann::json> registryTmp;
574 auto& registryOpt = (_registry) ? _registry : registryTmp;
575 if (!registryOpt)
Matt Spinler367144c2019-09-19 15:33:52 -0500576 {
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800577 registryOpt = readRegistry(_registryFile);
578 if (!registryOpt)
579 {
580 return std::nullopt;
581 }
582 else if (toCache)
583 {
584 // Save message registry in memory for peltool
585 _registry = std::move(registryTmp);
586 }
Matt Spinler367144c2019-09-19 15:33:52 -0500587 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800588 auto& reg = (_registry) ? _registry : registryTmp;
589 const auto& registry = reg.value();
Matt Spinler367144c2019-09-19 15:33:52 -0500590 // Find an entry with this name in the PEL array.
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800591 auto e = std::find_if(
592 registry["PELs"].begin(), registry["PELs"].end(),
593 [&name, &type](const auto& j) {
594 return ((name == j["Name"] && type == LookupType::name) ||
595 (name == j["SRC"]["ReasonCode"] &&
596 type == LookupType::reasonCode));
597 });
Matt Spinler367144c2019-09-19 15:33:52 -0500598
599 if (e != registry["PELs"].end())
600 {
601 // Fill in the Entry structure from the JSON. Most, but not all, fields
602 // are optional.
603
604 try
605 {
606 Entry entry;
607 entry.name = (*e)["Name"];
608 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]);
Matt Spinlere07f9152019-11-01 10:48:36 -0500609
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500610 if (e->contains("ActionFlags"))
Matt Spinlere07f9152019-11-01 10:48:36 -0500611 {
612 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]);
613 }
Matt Spinler367144c2019-09-19 15:33:52 -0500614
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500615 if (e->contains("MfgActionFlags"))
Matt Spinler367144c2019-09-19 15:33:52 -0500616 {
617 entry.mfgActionFlags =
618 helper::getActionFlags((*e)["MfgActionFlags"]);
619 }
620
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500621 if (e->contains("Severity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500622 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500623 entry.severity = helper::getSeverities((*e)["Severity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500624 }
625
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500626 if (e->contains("MfgSeverity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500627 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500628 entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500629 }
630
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500631 if (e->contains("EventType"))
Matt Spinler367144c2019-09-19 15:33:52 -0500632 {
633 entry.eventType = helper::getEventType((*e)["EventType"]);
634 }
635
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500636 if (e->contains("EventScope"))
Matt Spinler367144c2019-09-19 15:33:52 -0500637 {
638 entry.eventScope = helper::getEventScope((*e)["EventScope"]);
639 }
640
Matt Spinler93e29322019-09-20 11:16:15 -0500641 auto& src = (*e)["SRC"];
642 entry.src.reasonCode = helper::getSRCReasonCode(src, name);
643
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500644 if (src.contains("Type"))
Matt Spinler93e29322019-09-20 11:16:15 -0500645 {
646 entry.src.type = helper::getSRCType(src, name);
647 }
648 else
649 {
650 entry.src.type = static_cast<uint8_t>(SRCType::bmcError);
651 }
652
653 // Now that we know the SRC type and reason code,
654 // we can get the component ID.
655 entry.componentID = helper::getComponentID(
656 entry.src.type, entry.src.reasonCode, *e, name);
657
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500658 if (src.contains("Words6To9"))
Matt Spinler93e29322019-09-20 11:16:15 -0500659 {
660 entry.src.hexwordADFields =
661 helper::getSRCHexwordFields(src, name);
662 }
663
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500664 if (src.contains("SymptomIDFields"))
Matt Spinler93e29322019-09-20 11:16:15 -0500665 {
666 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
667 }
668
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500669 if (src.contains("PowerFault"))
Matt Spinler93e29322019-09-20 11:16:15 -0500670 {
671 entry.src.powerFault = src["PowerFault"];
672 }
Matt Spinler367144c2019-09-19 15:33:52 -0500673
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800674 auto& doc = (*e)["Documentation"];
675 entry.doc.message = doc["Message"];
676 entry.doc.description = doc["Description"];
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500677 if (doc.contains("MessageArgSources"))
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800678 {
679 entry.doc.messageArgSources = doc["MessageArgSources"];
680 }
681
Matt Spinlerd8e29002020-04-09 09:11:22 -0500682 // If there are callouts defined, save the JSON for later
683 if (_loadCallouts)
684 {
685 if (e->contains("Callouts"))
686 {
687 entry.callouts = (*e)["Callouts"];
688 }
689 else if (e->contains("CalloutsUsingAD"))
690 {
691 entry.callouts = (*e)["CalloutsUsingAD"];
692 }
693 }
694
Matt Spinler367144c2019-09-19 15:33:52 -0500695 return entry;
696 }
Patrick Williams66491c62021-10-06 12:23:37 -0500697 catch (const std::exception& e)
Matt Spinler367144c2019-09-19 15:33:52 -0500698 {
699 log<level::ERR>("Found invalid message registry field",
700 entry("ERROR=%s", e.what()));
701 }
702 }
703
704 return std::nullopt;
705}
706
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800707std::optional<nlohmann::json>
708 Registry::readRegistry(const std::filesystem::path& registryFile)
709{
710 // Look in /etc first in case someone put a test file there
711 fs::path debugFile{fs::path{debugFilePath} / registryFileName};
712 nlohmann::json registry;
713 std::ifstream file;
714
715 if (fs::exists(debugFile))
716 {
717 log<level::INFO>("Using debug PEL message registry");
718 file.open(debugFile);
719 }
720 else
721 {
722 file.open(registryFile);
723 }
724
725 try
726 {
727 registry = nlohmann::json::parse(file);
728 }
Patrick Williams66491c62021-10-06 12:23:37 -0500729 catch (const std::exception& e)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800730 {
731 log<level::ERR>("Error parsing message registry JSON",
732 entry("JSON_ERROR=%s", e.what()));
733 return std::nullopt;
734 }
735 return registry;
736}
737
Matt Spinler6b427cc2020-04-09 09:42:59 -0500738std::vector<RegistryCallout>
739 Registry::getCallouts(const nlohmann::json& calloutJSON,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500740 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500741 const AdditionalData& additionalData)
742{
743 // The JSON may either use an AdditionalData key
744 // as an index, or not.
745 if (helper::calloutUsesAdditionalData(calloutJSON))
746 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500747 return helper::getCalloutsUsingAD(calloutJSON, systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500748 additionalData);
749 }
750
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500751 return helper::getCalloutsWithoutAD(calloutJSON, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500752}
753
Matt Spinler367144c2019-09-19 15:33:52 -0500754} // namespace message
755} // namespace pels
756} // namespace openpower