blob: 250d7cd25c1fb3eb3d0242efab9416af27e7320c [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
18#include "pel_types.hpp"
19#include "pel_values.hpp"
20
Matt Spinler4f460312023-07-07 16:24:20 -050021#include <phosphor-logging/lg2.hpp>
Matt Spinler367144c2019-09-19 15:33:52 -050022
Arya K Padman15370292024-05-14 01:48:22 -050023#include <algorithm>
Patrick Williams2544b412022-10-04 08:41:06 -050024#include <fstream>
25
Matt Spinler367144c2019-09-19 15:33:52 -050026namespace openpower
27{
28namespace pels
29{
30namespace message
31{
32
33namespace pv = pel_values;
34namespace fs = std::filesystem;
Matt Spinler367144c2019-09-19 15:33:52 -050035
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.
Matt Spinler4f460312023-07-07 16:24:20 -050048 lg2::error("Invalid subsystem name used in message registry: {SUBSYS}",
49 "SUBSYS", subsystemName);
Matt Spinler367144c2019-09-19 15:33:52 -050050
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.
Matt Spinler4f460312023-07-07 16:24:20 -050063 lg2::error("Invalid severity name used in message registry: {SEV}",
64 "SEV", severityName);
Matt Spinler367144c2019-09-19 15:33:52 -050065
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.
Matt Spinler4f460312023-07-07 16:24:20 -0500118 lg2::error(
119 "Invalid action flag name used in message registry: {FLAG}",
120 "FLAG", flag);
Matt Spinler367144c2019-09-19 15:33:52 -0500121
122 throw std::runtime_error(
123 "Invalid action flag used in message registry");
124 }
125
126 actionFlags |= std::get<pv::fieldValuePos>(*s);
127 }
128
129 return actionFlags;
130}
131
132uint8_t getEventType(const std::string& eventTypeName)
133{
134 auto t = pv::findByName(eventTypeName, pv::eventTypeValues);
135 if (t == pv::eventTypeValues.end())
136 {
Matt Spinler4f460312023-07-07 16:24:20 -0500137 lg2::error("Invalid event type used in message registry: {TYPE}",
138 "TYPE", eventTypeName);
Matt Spinler367144c2019-09-19 15:33:52 -0500139
140 throw std::runtime_error("Invalid event type used in message registry");
141 }
142 return std::get<pv::fieldValuePos>(*t);
143}
144
145uint8_t getEventScope(const std::string& eventScopeName)
146{
147 auto s = pv::findByName(eventScopeName, pv::eventScopeValues);
148 if (s == pv::eventScopeValues.end())
149 {
Matt Spinler4f460312023-07-07 16:24:20 -0500150 lg2::error("Invalid event scope used in registry: {SCOPE}", "SCOPE",
151 eventScopeName);
Matt Spinler367144c2019-09-19 15:33:52 -0500152
153 throw std::runtime_error(
154 "Invalid event scope used in message registry");
155 }
156 return std::get<pv::fieldValuePos>(*s);
157}
158
Matt Spinler93e29322019-09-20 11:16:15 -0500159uint16_t getSRCReasonCode(const nlohmann::json& src, const std::string& name)
160{
161 std::string rc = src["ReasonCode"];
162 uint16_t reasonCode = strtoul(rc.c_str(), nullptr, 16);
163 if (reasonCode == 0)
164 {
Matt Spinler4f460312023-07-07 16:24:20 -0500165 lg2::error(
166 "Invalid reason code {RC} in message registry, error name = {ERROR}",
167 "RC", rc, "ERROR", name);
Matt Spinler93e29322019-09-20 11:16:15 -0500168
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 {
Matt Spinler4f460312023-07-07 16:24:20 -0500181 lg2::error(
182 "Invalid SRC Type {TYPE} in message registry, error name = {ERROR}",
183 "TYPE", srcType, "ERROR", name);
Matt Spinler93e29322019-09-20 11:16:15 -0500184
185 throw std::runtime_error("Invalid SRC Type in message registry");
186 }
187
188 return type;
189}
190
Matt Spinler3fe93e92023-04-14 14:06:59 -0500191bool getSRCDeconfigFlag(const nlohmann::json& src)
192{
193 return src["DeconfigFlag"].get<bool>();
194}
195
Matt Spinlerda5b76b2023-06-01 15:56:57 -0500196bool getSRCCheckstopFlag(const nlohmann::json& src)
197{
198 return src["CheckstopFlag"].get<bool>();
199}
200
Matt Spinler93e29322019-09-20 11:16:15 -0500201std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
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 {
Matt Spinler4f460312023-07-07 16:24:20 -0500224 lg2::error(
225 "Invalid SRC word number {NUM} in message registry, error name = {ERROR}",
226 "NUM", num, "ERROR", name);
Matt Spinler93e29322019-09-20 11:16:15 -0500227
228 throw std::runtime_error("Invalid SRC word in message registry");
229 }
230
231 auto attributes = word.value();
Zane Shelleye8db29b2021-11-13 10:34:07 -0600232
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
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800237 std::tuple<std::string, std::string> adPropSourceDesc(
Zane Shelleye8db29b2021-11-13 10:34:07 -0600238 attributes["AdditionalDataPropSource"], desc);
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800239 hexwordFields[wordNum] = std::move(adPropSourceDesc);
Matt Spinler93e29322019-09-20 11:16:15 -0500240 }
241
242 if (!hexwordFields.empty())
243 {
244 return hexwordFields;
245 }
246
247 return std::nullopt;
248}
Patrick Williams25291152025-02-01 08:21:42 -0500249std::optional<std::vector<SRC::WordNum>> getSRCSymptomIDFields(
250 const nlohmann::json& src, const std::string& name)
Matt Spinler93e29322019-09-20 11:16:15 -0500251{
252 std::vector<SRC::WordNum> symptomIDFields;
253
254 // Looks like:
255 // "SymptomIDFields": ["SRCWord3", "SRCWord6"],
256
Matt Spinler9d59d582021-05-19 07:57:10 -0600257 for (const std::string field : src["SymptomIDFields"])
Matt Spinler93e29322019-09-20 11:16:15 -0500258 {
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 {
Matt Spinler4f460312023-07-07 16:24:20 -0500265 lg2::error(
266 "Invalid symptom ID field {FIELD} in message registry, error name = {ERROR}",
267 "FIELD", field, "ERROR", name);
Matt Spinler93e29322019-09-20 11:16:15 -0500268
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
281uint16_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.
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500288 if (pelEntry.contains("ComponentID"))
Matt Spinler93e29322019-09-20 11:16:15 -0500289 {
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 {
Matt Spinler4f460312023-07-07 16:24:20 -0500303 lg2::error(
304 "Missing component ID field in message registry, error name = {ERROR}",
305 "ERROR", name);
Matt Spinler93e29322019-09-20 11:16:15 -0500306
307 throw std::runtime_error(
308 "Missing component ID field in message registry");
309 }
310 }
311
312 return id;
313}
314
Matt Spinler6b427cc2020-04-09 09:42:59 -0500315/**
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 */
323bool 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 *
Arya K Padman15370292024-05-14 01:48:22 -0500333 * 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.
Matt Spinler6b427cc2020-04-09 09:42:59 -0500346 *
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 * ]
Arya K Padman15370292024-05-14 01:48:22 -0500360 * },
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 * ]
Matt Spinler6b427cc2020-04-09 09:42:59 -0500374 * }
Arya K Padman15370292024-05-14 01:48:22 -0500375 *
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.
Matt Spinler6b427cc2020-04-09 09:42:59 -0500383 */
Arya K Padman15370292024-05-14 01:48:22 -0500384static void findCalloutList(const nlohmann::json& json,
385 const std::vector<std::string>& systemNames,
386 nlohmann::json& calloutLists)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500387{
Matt Spinler6b427cc2020-04-09 09:42:59 -0500388 if (!json.is_array())
389 {
390 throw std::runtime_error{"findCalloutList was not passed a JSON array"};
391 }
392
Arya K Padman15370292024-05-14 01:48:22 -0500393 // Flag to indicate whether system specific callouts found or not
394 bool foundCallouts = false;
395
396 for (const auto& callouts : json)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500397 {
Arya K Padman15370292024-05-14 01:48:22 -0500398 if (callouts.contains("System"))
Matt Spinler6b427cc2020-04-09 09:42:59 -0500399 {
Arya K Padman15370292024-05-14 01:48:22 -0500400 if (std::ranges::find(systemNames,
401 callouts["System"].get<std::string>()) !=
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500402 systemNames.end())
Matt Spinler6b427cc2020-04-09 09:42:59 -0500403 {
Arya K Padman15370292024-05-14 01:48:22 -0500404 calloutLists.insert(calloutLists.end(),
405 callouts["CalloutList"].begin(),
406 callouts["CalloutList"].end());
407 foundCallouts = true;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500408 }
Arya K Padman15370292024-05-14 01:48:22 -0500409 continue;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500410 }
Arya K Padman15370292024-05-14 01:48:22 -0500411
412 if (callouts.contains("Systems"))
Matt Spinler6b427cc2020-04-09 09:42:59 -0500413 {
Arya K Padman15370292024-05-14 01:48:22 -0500414 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());
Matt Spinler6b427cc2020-04-09 09:42:59 -0500436 }
437 }
Arya K Padman15370292024-05-14 01:48:22 -0500438 if (calloutLists.empty())
Matt Spinler6b427cc2020-04-09 09:42:59 -0500439 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500440 std::string types;
441 std::for_each(systemNames.begin(), systemNames.end(),
442 [&types](const auto& t) { types += t + '|'; });
Matt Spinler4f460312023-07-07 16:24:20 -0500443 lg2::warning(
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500444 "No matching system name entry or default system name entry "
Matt Spinler4f460312023-07-07 16:24:20 -0500445 " for PEL callout list, names = {TYPES}",
446 "TYPES", types);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500447
448 throw std::runtime_error{
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500449 "Could not find a CalloutList JSON for this error and system name"};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500450 }
Matt Spinler6b427cc2020-04-09 09:42:59 -0500451}
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 */
469RegistryCallout makeRegistryCallout(const nlohmann::json& json)
470{
471 RegistryCallout callout;
472
473 callout.priority = "high";
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500474 callout.useInventoryLocCode = false;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500475
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
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500500 if (json.contains("UseInventoryLocCode"))
501 {
502 callout.useInventoryLocCode = json["UseInventoryLocCode"].get<bool>();
503 }
504
Matt Spinler6b427cc2020-04-09 09:42:59 -0500505 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:
Arya K Padman15370292024-05-14 01:48:22 -0500517 * {
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 * }
Matt Spinler6b427cc2020-04-09 09:42:59 -0500545 *
546 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500547 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500548 *
549 * @return std::vector<RegistryCallout> - The callouts to use
550 */
Patrick Williams075c7922024-08-16 15:19:49 -0400551std::vector<RegistryCallout> getCalloutsWithoutAD(
552 const nlohmann::json& json, const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500553{
554 std::vector<RegistryCallout> calloutEntries;
555
Arya K Padman15370292024-05-14 01:48:22 -0500556 nlohmann::json calloutLists = nlohmann::json::array();
557
Matt Spinler6b427cc2020-04-09 09:42:59 -0500558 // Find the CalloutList to use based on the system type
Arya K Padman15370292024-05-14 01:48:22 -0500559 findCalloutList(json, systemNames, calloutLists);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500560
561 // We finally found the callouts, make the objects.
Arya K Padman15370292024-05-14 01:48:22 -0500562 for (const auto& callout : calloutLists)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500563 {
Matt Spinlerf904caf2025-05-09 11:46:45 -0500564 calloutEntries.push_back(makeRegistryCallout(callout));
Matt Spinler6b427cc2020-04-09 09:42:59 -0500565 }
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
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500601 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500602 * @param[in] additionalData - The AdditionalData property
603 *
604 * @return std::vector<RegistryCallout> - The callouts to use
605 */
Patrick Williams075c7922024-08-16 15:19:49 -0400606std::vector<RegistryCallout> getCalloutsUsingAD(
607 const nlohmann::json& json, const std::vector<std::string>& systemNames,
608 const AdditionalData& additionalData)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500609{
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
Matt Spinler4f460312023-07-07 16:24:20 -0500619 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);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500623 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
Patrick Williams075c7922024-08-16 15:19:49 -0400630 auto it = std::find_if(
631 callouts.begin(), callouts.end(), [adValue](const nlohmann::json& j) {
632 return *adValue == j["ADValue"].get<std::string>();
633 });
Matt Spinler6b427cc2020-04-09 09:42:59 -0500634
635 if (it == callouts.end())
636 {
Matt Spinlerf397afc2021-01-29 11:21:44 -0600637 // This can happen if not all possible values were in the
Matt Spinler3d923312022-08-01 09:52:55 -0500638 // 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 }
Matt Spinlerf397afc2021-01-29 11:21:44 -0600646 return std::vector<RegistryCallout>{};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500647 }
648
649 // Proceed to find the callouts possibly based on system type.
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500650 return getCalloutsWithoutAD((*it)["Callouts"], systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500651}
652
Matt Spinler711f1122022-12-15 11:41:20 -0600653/**
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 */
674JournalCapture 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 {
Matt Spinler4f460312023-07-07 16:24:20 -0500698 lg2::error("JournalCapture section not the right format");
Matt Spinler711f1122022-12-15 11:41:20 -0600699 throw std::runtime_error{"JournalCapture section not the right format"};
700 }
701
702 return capt;
703}
704
Matt Spinler367144c2019-09-19 15:33:52 -0500705} // namespace helper
706
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800707std::optional<Entry> Registry::lookup(const std::string& name, LookupType type,
708 bool toCache)
Matt Spinler367144c2019-09-19 15:33:52 -0500709{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800710 std::optional<nlohmann::json> registryTmp;
711 auto& registryOpt = (_registry) ? _registry : registryTmp;
712 if (!registryOpt)
Matt Spinler367144c2019-09-19 15:33:52 -0500713 {
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800714 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 }
Matt Spinler367144c2019-09-19 15:33:52 -0500724 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800725 auto& reg = (_registry) ? _registry : registryTmp;
726 const auto& registry = reg.value();
Matt Spinler367144c2019-09-19 15:33:52 -0500727 // Find an entry with this name in the PEL array.
Patrick Williams075c7922024-08-16 15:19:49 -0400728 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 });
Matt Spinler367144c2019-09-19 15:33:52 -0500736
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"];
Matt Spinler23970b02022-02-25 16:34:46 -0600746
747 if (e->contains("Subsystem"))
748 {
749 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]);
750 }
Matt Spinlere07f9152019-11-01 10:48:36 -0500751
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500752 if (e->contains("ActionFlags"))
Matt Spinlere07f9152019-11-01 10:48:36 -0500753 {
754 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]);
755 }
Matt Spinler367144c2019-09-19 15:33:52 -0500756
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500757 if (e->contains("MfgActionFlags"))
Matt Spinler367144c2019-09-19 15:33:52 -0500758 {
759 entry.mfgActionFlags =
760 helper::getActionFlags((*e)["MfgActionFlags"]);
761 }
762
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500763 if (e->contains("Severity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500764 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500765 entry.severity = helper::getSeverities((*e)["Severity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500766 }
767
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500768 if (e->contains("MfgSeverity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500769 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500770 entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500771 }
772
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500773 if (e->contains("EventType"))
Matt Spinler367144c2019-09-19 15:33:52 -0500774 {
775 entry.eventType = helper::getEventType((*e)["EventType"]);
776 }
777
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500778 if (e->contains("EventScope"))
Matt Spinler367144c2019-09-19 15:33:52 -0500779 {
780 entry.eventScope = helper::getEventScope((*e)["EventScope"]);
781 }
782
Matt Spinler93e29322019-09-20 11:16:15 -0500783 auto& src = (*e)["SRC"];
784 entry.src.reasonCode = helper::getSRCReasonCode(src, name);
785
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500786 if (src.contains("Type"))
Matt Spinler93e29322019-09-20 11:16:15 -0500787 {
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
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500800 if (src.contains("Words6To9"))
Matt Spinler93e29322019-09-20 11:16:15 -0500801 {
Patrick Williams075c7922024-08-16 15:19:49 -0400802 entry.src.hexwordADFields =
803 helper::getSRCHexwordFields(src, name);
Matt Spinler93e29322019-09-20 11:16:15 -0500804 }
805
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500806 if (src.contains("SymptomIDFields"))
Matt Spinler93e29322019-09-20 11:16:15 -0500807 {
808 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
809 }
810
Matt Spinler3fe93e92023-04-14 14:06:59 -0500811 if (src.contains("DeconfigFlag"))
812 {
813 entry.src.deconfigFlag = helper::getSRCDeconfigFlag(src);
814 }
815
Matt Spinlerda5b76b2023-06-01 15:56:57 -0500816 if (src.contains("CheckstopFlag"))
817 {
818 entry.src.checkstopFlag = helper::getSRCCheckstopFlag(src);
819 }
820
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800821 auto& doc = (*e)["Documentation"];
822 entry.doc.message = doc["Message"];
823 entry.doc.description = doc["Description"];
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500824 if (doc.contains("MessageArgSources"))
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800825 {
Matt Spinler3bb15b92022-04-27 09:32:10 -0500826 entry.doc.messageArgSources = doc["MessageArgSources"];
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800827 }
828
Matt Spinlerd8e29002020-04-09 09:11:22 -0500829 // 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
Matt Spinler711f1122022-12-15 11:41:20 -0600842 if (e->contains("JournalCapture"))
843 {
844 entry.journalCapture =
845 helper::getJournalCapture((*e)["JournalCapture"]);
846 }
847
Matt Spinler367144c2019-09-19 15:33:52 -0500848 return entry;
849 }
Matt Spinler45796e82022-07-01 11:25:27 -0500850 catch (const std::exception& ex)
Matt Spinler367144c2019-09-19 15:33:52 -0500851 {
Matt Spinler4f460312023-07-07 16:24:20 -0500852 lg2::error("Found invalid message registry field. Error: {ERROR}",
853 "ERROR", ex);
Matt Spinler367144c2019-09-19 15:33:52 -0500854 }
855 }
856
857 return std::nullopt;
858}
859
Patrick Williams25291152025-02-01 08:21:42 -0500860std::optional<nlohmann::json> Registry::readRegistry(
861 const std::filesystem::path& registryFile)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800862{
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 {
Matt Spinler4f460312023-07-07 16:24:20 -0500870 lg2::info("Using debug PEL message registry");
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800871 file.open(debugFile);
872 }
873 else
874 {
875 file.open(registryFile);
876 }
877
878 try
879 {
880 registry = nlohmann::json::parse(file);
881 }
Patrick Williams66491c62021-10-06 12:23:37 -0500882 catch (const std::exception& e)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800883 {
Matt Spinler4f460312023-07-07 16:24:20 -0500884 lg2::error("Error parsing message registry JSON. Error: {ERROR}",
885 "ERROR", e);
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800886 return std::nullopt;
887 }
888 return registry;
889}
890
Patrick Williams25291152025-02-01 08:21:42 -0500891std::vector<RegistryCallout> Registry::getCallouts(
892 const nlohmann::json& calloutJSON,
893 const std::vector<std::string>& systemNames,
894 const AdditionalData& additionalData)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500895{
896 // The JSON may either use an AdditionalData key
897 // as an index, or not.
898 if (helper::calloutUsesAdditionalData(calloutJSON))
899 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500900 return helper::getCalloutsUsingAD(calloutJSON, systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500901 additionalData);
902 }
903
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500904 return helper::getCalloutsWithoutAD(calloutJSON, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500905}
906
Matt Spinler367144c2019-09-19 15:33:52 -0500907} // namespace message
908} // namespace pels
909} // namespace openpower