blob: 6cc2bdc928dd63d164ebc131d089d13cd3887e62 [file] [log] [blame]
Alexander Hansen40fb5492025-10-28 17:56:12 +01001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright 2019 IBM Corporation
3
Matt Spinler367144c2019-09-19 15:33:52 -05004#include "registry.hpp"
5
6#include "pel_types.hpp"
7#include "pel_values.hpp"
8
Matt Spinler4f460312023-07-07 16:24:20 -05009#include <phosphor-logging/lg2.hpp>
Matt Spinler367144c2019-09-19 15:33:52 -050010
Arya K Padman15370292024-05-14 01:48:22 -050011#include <algorithm>
Patrick Williams2544b412022-10-04 08:41:06 -050012#include <fstream>
13
Matt Spinler367144c2019-09-19 15:33:52 -050014namespace openpower
15{
16namespace pels
17{
18namespace message
19{
20
21namespace pv = pel_values;
22namespace fs = std::filesystem;
Matt Spinler367144c2019-09-19 15:33:52 -050023
24constexpr auto debugFilePath = "/etc/phosphor-logging/";
25
26namespace helper
27{
28
29uint8_t getSubsystem(const std::string& subsystemName)
30{
31 // Get the actual value to use in the PEL for the string name
32 auto ss = pv::findByName(subsystemName, pv::subsystemValues);
33 if (ss == pv::subsystemValues.end())
34 {
35 // Schema validation should be catching this.
Matt Spinler4f460312023-07-07 16:24:20 -050036 lg2::error("Invalid subsystem name used in message registry: {SUBSYS}",
37 "SUBSYS", subsystemName);
Matt Spinler367144c2019-09-19 15:33:52 -050038
39 throw std::runtime_error("Invalid subsystem used in message registry");
40 }
41
42 return std::get<pv::fieldValuePos>(*ss);
43}
44
45uint8_t getSeverity(const std::string& severityName)
46{
47 auto s = pv::findByName(severityName, pv::severityValues);
48 if (s == pv::severityValues.end())
49 {
50 // Schema validation should be catching this.
Matt Spinler4f460312023-07-07 16:24:20 -050051 lg2::error("Invalid severity name used in message registry: {SEV}",
52 "SEV", severityName);
Matt Spinler367144c2019-09-19 15:33:52 -050053
54 throw std::runtime_error("Invalid severity used in message registry");
55 }
56
57 return std::get<pv::fieldValuePos>(*s);
58}
59
Matt Spinleraadccc82020-04-10 14:33:42 -050060std::vector<RegistrySeverity> getSeverities(const nlohmann::json& severity)
61{
62 std::vector<RegistrySeverity> severities;
63
64 // The plain string value, like "unrecoverable"
65 if (severity.is_string())
66 {
67 RegistrySeverity s;
68 s.severity = getSeverity(severity.get<std::string>());
69 severities.push_back(std::move(s));
70 }
71 else
72 {
73 // An array, with an element like:
74 // {
75 // "SevValue": "unrecoverable",
76 // "System", "systemA"
77 // }
78 for (const auto& sev : severity)
79 {
80 RegistrySeverity s;
81 s.severity = getSeverity(sev["SevValue"].get<std::string>());
82
83 if (sev.contains("System"))
84 {
85 s.system = sev["System"].get<std::string>();
86 }
87
88 severities.push_back(std::move(s));
89 }
90 }
91
92 return severities;
93}
94
Matt Spinler367144c2019-09-19 15:33:52 -050095uint16_t getActionFlags(const std::vector<std::string>& flags)
96{
97 uint16_t actionFlags = 0;
98
99 // Make the bitmask based on the array of flag names
100 for (const auto& flag : flags)
101 {
102 auto s = pv::findByName(flag, pv::actionFlagsValues);
103 if (s == pv::actionFlagsValues.end())
104 {
105 // Schema validation should be catching this.
Matt Spinler4f460312023-07-07 16:24:20 -0500106 lg2::error(
107 "Invalid action flag name used in message registry: {FLAG}",
108 "FLAG", flag);
Matt Spinler367144c2019-09-19 15:33:52 -0500109
110 throw std::runtime_error(
111 "Invalid action flag used in message registry");
112 }
113
114 actionFlags |= std::get<pv::fieldValuePos>(*s);
115 }
116
117 return actionFlags;
118}
119
120uint8_t getEventType(const std::string& eventTypeName)
121{
122 auto t = pv::findByName(eventTypeName, pv::eventTypeValues);
123 if (t == pv::eventTypeValues.end())
124 {
Matt Spinler4f460312023-07-07 16:24:20 -0500125 lg2::error("Invalid event type used in message registry: {TYPE}",
126 "TYPE", eventTypeName);
Matt Spinler367144c2019-09-19 15:33:52 -0500127
128 throw std::runtime_error("Invalid event type used in message registry");
129 }
130 return std::get<pv::fieldValuePos>(*t);
131}
132
133uint8_t getEventScope(const std::string& eventScopeName)
134{
135 auto s = pv::findByName(eventScopeName, pv::eventScopeValues);
136 if (s == pv::eventScopeValues.end())
137 {
Matt Spinler4f460312023-07-07 16:24:20 -0500138 lg2::error("Invalid event scope used in registry: {SCOPE}", "SCOPE",
139 eventScopeName);
Matt Spinler367144c2019-09-19 15:33:52 -0500140
141 throw std::runtime_error(
142 "Invalid event scope used in message registry");
143 }
144 return std::get<pv::fieldValuePos>(*s);
145}
146
Matt Spinler93e29322019-09-20 11:16:15 -0500147uint16_t getSRCReasonCode(const nlohmann::json& src, const std::string& name)
148{
149 std::string rc = src["ReasonCode"];
150 uint16_t reasonCode = strtoul(rc.c_str(), nullptr, 16);
151 if (reasonCode == 0)
152 {
Matt Spinler4f460312023-07-07 16:24:20 -0500153 lg2::error(
154 "Invalid reason code {RC} in message registry, error name = {ERROR}",
155 "RC", rc, "ERROR", name);
Matt Spinler93e29322019-09-20 11:16:15 -0500156
157 throw std::runtime_error("Invalid reason code in message registry");
158 }
159 return reasonCode;
160}
161
162uint8_t getSRCType(const nlohmann::json& src, const std::string& name)
163{
164 // Looks like: "22"
165 std::string srcType = src["Type"];
166 size_t type = strtoul(srcType.c_str(), nullptr, 16);
167 if ((type == 0) || (srcType.size() != 2)) // 1 hex byte
168 {
Matt Spinler4f460312023-07-07 16:24:20 -0500169 lg2::error(
170 "Invalid SRC Type {TYPE} in message registry, error name = {ERROR}",
171 "TYPE", srcType, "ERROR", name);
Matt Spinler93e29322019-09-20 11:16:15 -0500172
173 throw std::runtime_error("Invalid SRC Type in message registry");
174 }
175
176 return type;
177}
178
Matt Spinler3fe93e92023-04-14 14:06:59 -0500179bool getSRCDeconfigFlag(const nlohmann::json& src)
180{
181 return src["DeconfigFlag"].get<bool>();
182}
183
Matt Spinlerda5b76b2023-06-01 15:56:57 -0500184bool getSRCCheckstopFlag(const nlohmann::json& src)
185{
186 return src["CheckstopFlag"].get<bool>();
187}
188
Matt Spinler93e29322019-09-20 11:16:15 -0500189std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
190 getSRCHexwordFields(const nlohmann::json& src, const std::string& name)
191{
192 std::map<SRC::WordNum, SRC::AdditionalDataField> hexwordFields;
193
194 // Build the map of which AdditionalData fields to use for which SRC words
195
196 // Like:
197 // {
198 // "8":
199 // {
200 // "AdditionalDataPropSource": "TEST"
201 // }
202 //
203 // }
204
205 for (const auto& word : src["Words6To9"].items())
206 {
207 std::string num = word.key();
208 size_t wordNum = std::strtoul(num.c_str(), nullptr, 10);
209
210 if (wordNum == 0)
211 {
Matt Spinler4f460312023-07-07 16:24:20 -0500212 lg2::error(
213 "Invalid SRC word number {NUM} in message registry, error name = {ERROR}",
214 "NUM", num, "ERROR", name);
Matt Spinler93e29322019-09-20 11:16:15 -0500215
216 throw std::runtime_error("Invalid SRC word in message registry");
217 }
218
219 auto attributes = word.value();
Zane Shelleye8db29b2021-11-13 10:34:07 -0600220
221 // Use an empty string for the description if it does not exist.
222 auto itr = attributes.find("Description");
223 std::string desc = (attributes.end() != itr) ? *itr : "";
224
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800225 std::tuple<std::string, std::string> adPropSourceDesc(
Zane Shelleye8db29b2021-11-13 10:34:07 -0600226 attributes["AdditionalDataPropSource"], desc);
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800227 hexwordFields[wordNum] = std::move(adPropSourceDesc);
Matt Spinler93e29322019-09-20 11:16:15 -0500228 }
229
230 if (!hexwordFields.empty())
231 {
232 return hexwordFields;
233 }
234
235 return std::nullopt;
236}
Patrick Williams25291152025-02-01 08:21:42 -0500237std::optional<std::vector<SRC::WordNum>> getSRCSymptomIDFields(
238 const nlohmann::json& src, const std::string& name)
Matt Spinler93e29322019-09-20 11:16:15 -0500239{
240 std::vector<SRC::WordNum> symptomIDFields;
241
242 // Looks like:
243 // "SymptomIDFields": ["SRCWord3", "SRCWord6"],
244
Matt Spinler9d59d582021-05-19 07:57:10 -0600245 for (const std::string field : src["SymptomIDFields"])
Matt Spinler93e29322019-09-20 11:16:15 -0500246 {
247 // Just need the last digit off the end, e.g. SRCWord6.
248 // The schema enforces the format of these.
249 auto srcWordNum = field.substr(field.size() - 1);
250 size_t num = std::strtoul(srcWordNum.c_str(), nullptr, 10);
251 if (num == 0)
252 {
Matt Spinler4f460312023-07-07 16:24:20 -0500253 lg2::error(
254 "Invalid symptom ID field {FIELD} in message registry, error name = {ERROR}",
255 "FIELD", field, "ERROR", name);
Matt Spinler93e29322019-09-20 11:16:15 -0500256
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 {
Matt Spinler4f460312023-07-07 16:24:20 -0500291 lg2::error(
292 "Missing component ID field in message registry, error name = {ERROR}",
293 "ERROR", name);
Matt Spinler93e29322019-09-20 11:16:15 -0500294
295 throw std::runtime_error(
296 "Missing component ID field in message registry");
297 }
298 }
299
300 return id;
301}
302
Matt Spinler6b427cc2020-04-09 09:42:59 -0500303/**
304 * @brief Says if the JSON is the format that contains AdditionalData keys
305 * as in index into them.
306 *
307 * @param[in] json - The highest level callout JSON
308 *
309 * @return bool - If it is the AdditionalData format or not
310 */
311bool calloutUsesAdditionalData(const nlohmann::json& json)
312{
313 return (json.contains("ADName") &&
314 json.contains("CalloutsWithTheirADValues"));
315}
316
317/**
318 * @brief Finds the callouts to use when there is no AdditionalData,
319 * but the system type may be used as a key.
320 *
Arya K Padman15370292024-05-14 01:48:22 -0500321 * A sample calloutList array looks like the following. The System and Systems
322 * key are optional.
323 *
324 * System key - Value of the key will be the system name as a string. The
325 * callouts for a specific system can define under this key.
326 *
327 * Systems key - Value of the key will be an array of system names in the form
328 * of string. The callouts common to the systems mentioned in the array can
329 * define under this key.
330 *
331 * If both System and Systems not present it means that entry applies to every
332 * configuration that doesn't have another entry with a matching System and
333 * Systems key.
Matt Spinler6b427cc2020-04-09 09:42:59 -0500334 *
335 * {
336 * "System": "system1",
337 * "CalloutList":
338 * [
339 * {
340 * "Priority": "high",
341 * "LocCode": "P1-C1"
342 * },
343 * {
344 * "Priority": "low",
345 * "LocCode": "P1"
346 * }
347 * ]
Arya K Padman15370292024-05-14 01:48:22 -0500348 * },
349 * {
350 * "Systems": ["system1", 'system2"],
351 * "CalloutList":
352 * [
353 * {
354 * "Priority": "high",
355 * "LocCode": "P0-C1"
356 * },
357 * {
358 * "Priority": "low",
359 * "LocCode": "P0"
360 * }
361 * ]
Matt Spinler6b427cc2020-04-09 09:42:59 -0500362 * }
Arya K Padman15370292024-05-14 01:48:22 -0500363 *
364 * @param[in] json - The callout JSON
365 * @param[in] systemNames - List of compatible system type names
366 * @param[out] calloutLists - The JSON array which will hold the calloutlist to
367 * use specific to the system.
368 *
369 * @return - Throws runtime exception if json is not an array or if calloutLists
370 * is empty.
Matt Spinler6b427cc2020-04-09 09:42:59 -0500371 */
Arya K Padman15370292024-05-14 01:48:22 -0500372static void findCalloutList(const nlohmann::json& json,
373 const std::vector<std::string>& systemNames,
374 nlohmann::json& calloutLists)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500375{
Matt Spinler6b427cc2020-04-09 09:42:59 -0500376 if (!json.is_array())
377 {
378 throw std::runtime_error{"findCalloutList was not passed a JSON array"};
379 }
380
Arya K Padman15370292024-05-14 01:48:22 -0500381 // Flag to indicate whether system specific callouts found or not
382 bool foundCallouts = false;
383
384 for (const auto& callouts : json)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500385 {
Arya K Padman15370292024-05-14 01:48:22 -0500386 if (callouts.contains("System"))
Matt Spinler6b427cc2020-04-09 09:42:59 -0500387 {
Arya K Padman15370292024-05-14 01:48:22 -0500388 if (std::ranges::find(systemNames,
389 callouts["System"].get<std::string>()) !=
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500390 systemNames.end())
Matt Spinler6b427cc2020-04-09 09:42:59 -0500391 {
Arya K Padman15370292024-05-14 01:48:22 -0500392 calloutLists.insert(calloutLists.end(),
393 callouts["CalloutList"].begin(),
394 callouts["CalloutList"].end());
395 foundCallouts = true;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500396 }
Arya K Padman15370292024-05-14 01:48:22 -0500397 continue;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500398 }
Arya K Padman15370292024-05-14 01:48:22 -0500399
400 if (callouts.contains("Systems"))
Matt Spinler6b427cc2020-04-09 09:42:59 -0500401 {
Arya K Padman15370292024-05-14 01:48:22 -0500402 std::vector<std::string> systems =
403 callouts["Systems"].get<std::vector<std::string>>();
404 auto inSystemNames = [systemNames](const auto& system) {
405 return (std::ranges::find(systemNames, system) !=
406 systemNames.end());
407 };
408 if (std::ranges::any_of(systems, inSystemNames))
409 {
410 calloutLists.insert(calloutLists.end(),
411 callouts["CalloutList"].begin(),
412 callouts["CalloutList"].end());
413 foundCallouts = true;
414 }
415 continue;
416 }
417
418 // Any entry if neither System/Systems key matches with system name
419 if (!foundCallouts)
420 {
421 calloutLists.insert(calloutLists.end(),
422 callouts["CalloutList"].begin(),
423 callouts["CalloutList"].end());
Matt Spinler6b427cc2020-04-09 09:42:59 -0500424 }
425 }
Arya K Padman15370292024-05-14 01:48:22 -0500426 if (calloutLists.empty())
Matt Spinler6b427cc2020-04-09 09:42:59 -0500427 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500428 std::string types;
429 std::for_each(systemNames.begin(), systemNames.end(),
430 [&types](const auto& t) { types += t + '|'; });
Matt Spinler4f460312023-07-07 16:24:20 -0500431 lg2::warning(
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500432 "No matching system name entry or default system name entry "
Matt Spinler4f460312023-07-07 16:24:20 -0500433 " for PEL callout list, names = {TYPES}",
434 "TYPES", types);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500435
436 throw std::runtime_error{
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500437 "Could not find a CalloutList JSON for this error and system name"};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500438 }
Matt Spinler6b427cc2020-04-09 09:42:59 -0500439}
440
441/**
442 * @brief Creates a RegistryCallout based on the input JSON.
443 *
444 * The JSON looks like:
445 * {
446 * "Priority": "high",
447 * "LocCode": "E1"
448 * ...
449 * }
450 *
451 * Schema validation enforces what keys are present.
452 *
453 * @param[in] json - The JSON dictionary entry for a callout
454 *
455 * @return RegistryCallout - A filled in RegistryCallout
456 */
457RegistryCallout makeRegistryCallout(const nlohmann::json& json)
458{
459 RegistryCallout callout;
460
461 callout.priority = "high";
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500462 callout.useInventoryLocCode = false;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500463
464 if (json.contains("Priority"))
465 {
466 callout.priority = json["Priority"].get<std::string>();
467 }
468
469 if (json.contains("LocCode"))
470 {
471 callout.locCode = json["LocCode"].get<std::string>();
472 }
473
474 if (json.contains("Procedure"))
475 {
476 callout.procedure = json["Procedure"].get<std::string>();
477 }
478 else if (json.contains("SymbolicFRU"))
479 {
480 callout.symbolicFRU = json["SymbolicFRU"].get<std::string>();
481 }
482 else if (json.contains("SymbolicFRUTrusted"))
483 {
484 callout.symbolicFRUTrusted =
485 json["SymbolicFRUTrusted"].get<std::string>();
486 }
487
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500488 if (json.contains("UseInventoryLocCode"))
489 {
490 callout.useInventoryLocCode = json["UseInventoryLocCode"].get<bool>();
491 }
492
Matt Spinler6b427cc2020-04-09 09:42:59 -0500493 return callout;
494}
495
496/**
497 * @brief Returns the callouts to use when an AdditionalData key is
498 * required to find the correct entries.
499 *
500 * The System property is used to find which CalloutList to use.
501 * If System is missing, then that CalloutList is valid for
502 * everything.
503 *
504 * The JSON looks like:
Arya K Padman15370292024-05-14 01:48:22 -0500505 * {
506 * "System": "system1",
507 * "CalloutList":
508 * [
509 * {
510 * "Priority": "high",
511 * "LocCode": "P1-C1"
512 * },
513 * {
514 * "Priority": "low",
515 * "LocCode": "P1"
516 * }
517 * ]
518 * },
519 * {
520 * "Systems": ["system1", 'system2"],
521 * "CalloutList":
522 * [
523 * {
524 * "Priority": "high",
525 * "LocCode": "P0-C1"
526 * },
527 * {
528 * "Priority": "low",
529 * "LocCode": "P0"
530 * }
531 * ]
532 * }
Matt Spinler6b427cc2020-04-09 09:42:59 -0500533 *
534 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500535 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500536 *
537 * @return std::vector<RegistryCallout> - The callouts to use
538 */
Patrick Williams075c7922024-08-16 15:19:49 -0400539std::vector<RegistryCallout> getCalloutsWithoutAD(
540 const nlohmann::json& json, const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500541{
542 std::vector<RegistryCallout> calloutEntries;
543
Arya K Padman15370292024-05-14 01:48:22 -0500544 nlohmann::json calloutLists = nlohmann::json::array();
545
Matt Spinler6b427cc2020-04-09 09:42:59 -0500546 // Find the CalloutList to use based on the system type
Arya K Padman15370292024-05-14 01:48:22 -0500547 findCalloutList(json, systemNames, calloutLists);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500548
549 // We finally found the callouts, make the objects.
Arya K Padman15370292024-05-14 01:48:22 -0500550 for (const auto& callout : calloutLists)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500551 {
Matt Spinlerf904caf2025-05-09 11:46:45 -0500552 calloutEntries.push_back(makeRegistryCallout(callout));
Matt Spinler6b427cc2020-04-09 09:42:59 -0500553 }
554
555 return calloutEntries;
556}
557
558/**
559 * @brief Returns the callouts to use when an AdditionalData key is
560 * required to find the correct entries.
561 *
562 * The JSON looks like:
563 * {
564 * "ADName": "PROC_NUM",
565 * "CalloutsWithTheirADValues":
566 * [
567 * {
568 * "ADValue": "0",
569 * "Callouts":
570 * [
571 * {
572 * "CalloutList":
573 * [
574 * {
575 * "Priority": "high",
576 * "LocCode": "P1-C5"
577 * }
578 * ]
579 * }
580 * ]
581 * }
582 * ]
583 * }
584 *
585 * Note that the "Callouts" entry above is the same as the top level
586 * entry used when there is no AdditionalData key.
587 *
588 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500589 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500590 * @param[in] additionalData - The AdditionalData property
591 *
592 * @return std::vector<RegistryCallout> - The callouts to use
593 */
Patrick Williams075c7922024-08-16 15:19:49 -0400594std::vector<RegistryCallout> getCalloutsUsingAD(
595 const nlohmann::json& json, const std::vector<std::string>& systemNames,
596 const AdditionalData& additionalData)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500597{
598 // This indicates which AD field we'll be using
599 auto keyName = json["ADName"].get<std::string>();
600
601 // Get the actual value from the AD data
602 auto adValue = additionalData.getValue(keyName);
603
604 if (!adValue)
605 {
606 // The AdditionalData did not contain the necessary key
Matt Spinler4f460312023-07-07 16:24:20 -0500607 lg2::warning("The PEL message registry callouts JSON "
608 "said to use an AdditionalData key that isn't in the "
609 "AdditionalData event log property, key = {KEY}",
610 "KEY", keyName);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500611 throw std::runtime_error{
612 "Missing AdditionalData entry for this callout"};
613 }
614
615 const auto& callouts = json["CalloutsWithTheirADValues"];
616
617 // find the entry with that AD value
Patrick Williams075c7922024-08-16 15:19:49 -0400618 auto it = std::find_if(
619 callouts.begin(), callouts.end(), [adValue](const nlohmann::json& j) {
620 return *adValue == j["ADValue"].get<std::string>();
621 });
Matt Spinler6b427cc2020-04-09 09:42:59 -0500622
623 if (it == callouts.end())
624 {
Matt Spinlerf397afc2021-01-29 11:21:44 -0600625 // This can happen if not all possible values were in the
Matt Spinler3d923312022-08-01 09:52:55 -0500626 // message registry and that's fine. There may be a
627 // "CalloutsWhenNoADMatch" section that contains callouts
628 // to use in this case.
629 if (json.contains("CalloutsWhenNoADMatch"))
630 {
631 return getCalloutsWithoutAD(json["CalloutsWhenNoADMatch"],
632 systemNames);
633 }
Matt Spinlerf397afc2021-01-29 11:21:44 -0600634 return std::vector<RegistryCallout>{};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500635 }
636
637 // Proceed to find the callouts possibly based on system type.
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500638 return getCalloutsWithoutAD((*it)["Callouts"], systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500639}
640
Matt Spinler711f1122022-12-15 11:41:20 -0600641/**
642 * @brief Returns the journal capture information
643 *
644 * The JSON looks like:
645 * "JournalCapture": {
646 * "NumLines": 30
647 * }
648 *
649 * "JournalCapture":
650 * {
651 * "Sections": [
652 * {
653 * "SyslogID": "phosphor-log-manager",
654 * "NumLines": 20
655 * }
656 * ]
657 * }
658 *
659 * @param json - The journal capture JSON
660 * @return JournalCapture - The filled in variant
661 */
662JournalCapture getJournalCapture(const nlohmann::json& json)
663{
664 JournalCapture capt;
665
666 // Primary key is either NumLines or Sections.
667 if (json.contains("NumLines"))
668 {
669 capt = json.at("NumLines").get<size_t>();
670 }
671 else if (json.contains("Sections"))
672 {
673 AppCaptureList captures;
674 for (const auto& capture : json.at("Sections"))
675 {
676 AppCapture ac;
677 ac.syslogID = capture.at("SyslogID").get<std::string>();
678 ac.numLines = capture.at("NumLines").get<size_t>();
679 captures.push_back(std::move(ac));
680 }
681
682 capt = captures;
683 }
684 else
685 {
Matt Spinler4f460312023-07-07 16:24:20 -0500686 lg2::error("JournalCapture section not the right format");
Matt Spinler711f1122022-12-15 11:41:20 -0600687 throw std::runtime_error{"JournalCapture section not the right format"};
688 }
689
690 return capt;
691}
692
Matt Spinler367144c2019-09-19 15:33:52 -0500693} // namespace helper
694
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800695std::optional<Entry> Registry::lookup(const std::string& name, LookupType type,
696 bool toCache)
Matt Spinler367144c2019-09-19 15:33:52 -0500697{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800698 std::optional<nlohmann::json> registryTmp;
699 auto& registryOpt = (_registry) ? _registry : registryTmp;
700 if (!registryOpt)
Matt Spinler367144c2019-09-19 15:33:52 -0500701 {
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800702 registryOpt = readRegistry(_registryFile);
703 if (!registryOpt)
704 {
705 return std::nullopt;
706 }
707 else if (toCache)
708 {
709 // Save message registry in memory for peltool
710 _registry = std::move(registryTmp);
711 }
Matt Spinler367144c2019-09-19 15:33:52 -0500712 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800713 auto& reg = (_registry) ? _registry : registryTmp;
714 const auto& registry = reg.value();
Matt Spinler367144c2019-09-19 15:33:52 -0500715 // Find an entry with this name in the PEL array.
Patrick Williams075c7922024-08-16 15:19:49 -0400716 auto e = std::find_if(
717 registry["PELs"].begin(), registry["PELs"].end(),
718 [&name, &type](const nlohmann::json& j) {
719 return ((name == j.at("Name").get<std::string>() &&
720 type == LookupType::name) ||
721 (name == j.at("SRC").at("ReasonCode").get<std::string>() &&
722 type == LookupType::reasonCode));
723 });
Matt Spinler367144c2019-09-19 15:33:52 -0500724
725 if (e != registry["PELs"].end())
726 {
727 // Fill in the Entry structure from the JSON. Most, but not all, fields
728 // are optional.
729
730 try
731 {
732 Entry entry;
733 entry.name = (*e)["Name"];
Matt Spinler23970b02022-02-25 16:34:46 -0600734
735 if (e->contains("Subsystem"))
736 {
737 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]);
738 }
Matt Spinlere07f9152019-11-01 10:48:36 -0500739
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500740 if (e->contains("ActionFlags"))
Matt Spinlere07f9152019-11-01 10:48:36 -0500741 {
742 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]);
743 }
Matt Spinler367144c2019-09-19 15:33:52 -0500744
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500745 if (e->contains("MfgActionFlags"))
Matt Spinler367144c2019-09-19 15:33:52 -0500746 {
747 entry.mfgActionFlags =
748 helper::getActionFlags((*e)["MfgActionFlags"]);
749 }
750
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500751 if (e->contains("Severity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500752 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500753 entry.severity = helper::getSeverities((*e)["Severity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500754 }
755
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500756 if (e->contains("MfgSeverity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500757 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500758 entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500759 }
760
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500761 if (e->contains("EventType"))
Matt Spinler367144c2019-09-19 15:33:52 -0500762 {
763 entry.eventType = helper::getEventType((*e)["EventType"]);
764 }
765
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500766 if (e->contains("EventScope"))
Matt Spinler367144c2019-09-19 15:33:52 -0500767 {
768 entry.eventScope = helper::getEventScope((*e)["EventScope"]);
769 }
770
Matt Spinler93e29322019-09-20 11:16:15 -0500771 auto& src = (*e)["SRC"];
772 entry.src.reasonCode = helper::getSRCReasonCode(src, name);
773
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500774 if (src.contains("Type"))
Matt Spinler93e29322019-09-20 11:16:15 -0500775 {
776 entry.src.type = helper::getSRCType(src, name);
777 }
778 else
779 {
780 entry.src.type = static_cast<uint8_t>(SRCType::bmcError);
781 }
782
783 // Now that we know the SRC type and reason code,
784 // we can get the component ID.
785 entry.componentID = helper::getComponentID(
786 entry.src.type, entry.src.reasonCode, *e, name);
787
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500788 if (src.contains("Words6To9"))
Matt Spinler93e29322019-09-20 11:16:15 -0500789 {
Patrick Williams075c7922024-08-16 15:19:49 -0400790 entry.src.hexwordADFields =
791 helper::getSRCHexwordFields(src, name);
Matt Spinler93e29322019-09-20 11:16:15 -0500792 }
793
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500794 if (src.contains("SymptomIDFields"))
Matt Spinler93e29322019-09-20 11:16:15 -0500795 {
796 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
797 }
798
Matt Spinler3fe93e92023-04-14 14:06:59 -0500799 if (src.contains("DeconfigFlag"))
800 {
801 entry.src.deconfigFlag = helper::getSRCDeconfigFlag(src);
802 }
803
Matt Spinlerda5b76b2023-06-01 15:56:57 -0500804 if (src.contains("CheckstopFlag"))
805 {
806 entry.src.checkstopFlag = helper::getSRCCheckstopFlag(src);
807 }
808
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800809 auto& doc = (*e)["Documentation"];
810 entry.doc.message = doc["Message"];
811 entry.doc.description = doc["Description"];
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500812 if (doc.contains("MessageArgSources"))
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800813 {
Matt Spinler3bb15b92022-04-27 09:32:10 -0500814 entry.doc.messageArgSources = doc["MessageArgSources"];
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800815 }
816
Matt Spinlerd8e29002020-04-09 09:11:22 -0500817 // If there are callouts defined, save the JSON for later
818 if (_loadCallouts)
819 {
820 if (e->contains("Callouts"))
821 {
822 entry.callouts = (*e)["Callouts"];
823 }
824 else if (e->contains("CalloutsUsingAD"))
825 {
826 entry.callouts = (*e)["CalloutsUsingAD"];
827 }
828 }
829
Matt Spinler711f1122022-12-15 11:41:20 -0600830 if (e->contains("JournalCapture"))
831 {
832 entry.journalCapture =
833 helper::getJournalCapture((*e)["JournalCapture"]);
834 }
835
Matt Spinler367144c2019-09-19 15:33:52 -0500836 return entry;
837 }
Matt Spinler45796e82022-07-01 11:25:27 -0500838 catch (const std::exception& ex)
Matt Spinler367144c2019-09-19 15:33:52 -0500839 {
Matt Spinler4f460312023-07-07 16:24:20 -0500840 lg2::error("Found invalid message registry field. Error: {ERROR}",
841 "ERROR", ex);
Matt Spinler367144c2019-09-19 15:33:52 -0500842 }
843 }
844
845 return std::nullopt;
846}
847
Patrick Williams25291152025-02-01 08:21:42 -0500848std::optional<nlohmann::json> Registry::readRegistry(
849 const std::filesystem::path& registryFile)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800850{
851 // Look in /etc first in case someone put a test file there
852 fs::path debugFile{fs::path{debugFilePath} / registryFileName};
853 nlohmann::json registry;
854 std::ifstream file;
855
856 if (fs::exists(debugFile))
857 {
Matt Spinler4f460312023-07-07 16:24:20 -0500858 lg2::info("Using debug PEL message registry");
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800859 file.open(debugFile);
860 }
861 else
862 {
863 file.open(registryFile);
864 }
865
866 try
867 {
868 registry = nlohmann::json::parse(file);
869 }
Patrick Williams66491c62021-10-06 12:23:37 -0500870 catch (const std::exception& e)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800871 {
Matt Spinler4f460312023-07-07 16:24:20 -0500872 lg2::error("Error parsing message registry JSON. Error: {ERROR}",
873 "ERROR", e);
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800874 return std::nullopt;
875 }
876 return registry;
877}
878
Patrick Williams25291152025-02-01 08:21:42 -0500879std::vector<RegistryCallout> Registry::getCallouts(
880 const nlohmann::json& calloutJSON,
881 const std::vector<std::string>& systemNames,
882 const AdditionalData& additionalData)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500883{
884 // The JSON may either use an AdditionalData key
885 // as an index, or not.
886 if (helper::calloutUsesAdditionalData(calloutJSON))
887 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500888 return helper::getCalloutsUsingAD(calloutJSON, systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500889 additionalData);
890 }
891
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500892 return helper::getCalloutsWithoutAD(calloutJSON, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500893}
894
Matt Spinler367144c2019-09-19 15:33:52 -0500895} // namespace message
896} // namespace pels
897} // namespace openpower