blob: f96c04ef5c8222e086ff5cb770e496586069d04d [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
Matt Spinler4f460312023-07-07 16:24:20 -050022#include <phosphor-logging/lg2.hpp>
Matt Spinler367144c2019-09-19 15:33:52 -050023
Arya K Padman15370292024-05-14 01:48:22 -050024#include <algorithm>
Patrick Williams2544b412022-10-04 08:41:06 -050025#include <fstream>
26
Matt Spinler367144c2019-09-19 15:33:52 -050027namespace openpower
28{
29namespace pels
30{
31namespace message
32{
33
34namespace pv = pel_values;
35namespace fs = std::filesystem;
Matt Spinler367144c2019-09-19 15:33:52 -050036
37constexpr auto debugFilePath = "/etc/phosphor-logging/";
38
39namespace helper
40{
41
42uint8_t getSubsystem(const std::string& subsystemName)
43{
44 // Get the actual value to use in the PEL for the string name
45 auto ss = pv::findByName(subsystemName, pv::subsystemValues);
46 if (ss == pv::subsystemValues.end())
47 {
48 // Schema validation should be catching this.
Matt Spinler4f460312023-07-07 16:24:20 -050049 lg2::error("Invalid subsystem name used in message registry: {SUBSYS}",
50 "SUBSYS", subsystemName);
Matt Spinler367144c2019-09-19 15:33:52 -050051
52 throw std::runtime_error("Invalid subsystem used in message registry");
53 }
54
55 return std::get<pv::fieldValuePos>(*ss);
56}
57
58uint8_t getSeverity(const std::string& severityName)
59{
60 auto s = pv::findByName(severityName, pv::severityValues);
61 if (s == pv::severityValues.end())
62 {
63 // Schema validation should be catching this.
Matt Spinler4f460312023-07-07 16:24:20 -050064 lg2::error("Invalid severity name used in message registry: {SEV}",
65 "SEV", severityName);
Matt Spinler367144c2019-09-19 15:33:52 -050066
67 throw std::runtime_error("Invalid severity used in message registry");
68 }
69
70 return std::get<pv::fieldValuePos>(*s);
71}
72
Matt Spinleraadccc82020-04-10 14:33:42 -050073std::vector<RegistrySeverity> getSeverities(const nlohmann::json& severity)
74{
75 std::vector<RegistrySeverity> severities;
76
77 // The plain string value, like "unrecoverable"
78 if (severity.is_string())
79 {
80 RegistrySeverity s;
81 s.severity = getSeverity(severity.get<std::string>());
82 severities.push_back(std::move(s));
83 }
84 else
85 {
86 // An array, with an element like:
87 // {
88 // "SevValue": "unrecoverable",
89 // "System", "systemA"
90 // }
91 for (const auto& sev : severity)
92 {
93 RegistrySeverity s;
94 s.severity = getSeverity(sev["SevValue"].get<std::string>());
95
96 if (sev.contains("System"))
97 {
98 s.system = sev["System"].get<std::string>();
99 }
100
101 severities.push_back(std::move(s));
102 }
103 }
104
105 return severities;
106}
107
Matt Spinler367144c2019-09-19 15:33:52 -0500108uint16_t getActionFlags(const std::vector<std::string>& flags)
109{
110 uint16_t actionFlags = 0;
111
112 // Make the bitmask based on the array of flag names
113 for (const auto& flag : flags)
114 {
115 auto s = pv::findByName(flag, pv::actionFlagsValues);
116 if (s == pv::actionFlagsValues.end())
117 {
118 // Schema validation should be catching this.
Matt Spinler4f460312023-07-07 16:24:20 -0500119 lg2::error(
120 "Invalid action flag name used in message registry: {FLAG}",
121 "FLAG", flag);
Matt Spinler367144c2019-09-19 15:33:52 -0500122
123 throw std::runtime_error(
124 "Invalid action flag used in message registry");
125 }
126
127 actionFlags |= std::get<pv::fieldValuePos>(*s);
128 }
129
130 return actionFlags;
131}
132
133uint8_t getEventType(const std::string& eventTypeName)
134{
135 auto t = pv::findByName(eventTypeName, pv::eventTypeValues);
136 if (t == pv::eventTypeValues.end())
137 {
Matt Spinler4f460312023-07-07 16:24:20 -0500138 lg2::error("Invalid event type used in message registry: {TYPE}",
139 "TYPE", eventTypeName);
Matt Spinler367144c2019-09-19 15:33:52 -0500140
141 throw std::runtime_error("Invalid event type used in message registry");
142 }
143 return std::get<pv::fieldValuePos>(*t);
144}
145
146uint8_t getEventScope(const std::string& eventScopeName)
147{
148 auto s = pv::findByName(eventScopeName, pv::eventScopeValues);
149 if (s == pv::eventScopeValues.end())
150 {
Matt Spinler4f460312023-07-07 16:24:20 -0500151 lg2::error("Invalid event scope used in registry: {SCOPE}", "SCOPE",
152 eventScopeName);
Matt Spinler367144c2019-09-19 15:33:52 -0500153
154 throw std::runtime_error(
155 "Invalid event scope used in message registry");
156 }
157 return std::get<pv::fieldValuePos>(*s);
158}
159
Matt Spinler93e29322019-09-20 11:16:15 -0500160uint16_t getSRCReasonCode(const nlohmann::json& src, const std::string& name)
161{
162 std::string rc = src["ReasonCode"];
163 uint16_t reasonCode = strtoul(rc.c_str(), nullptr, 16);
164 if (reasonCode == 0)
165 {
Matt Spinler4f460312023-07-07 16:24:20 -0500166 lg2::error(
167 "Invalid reason code {RC} in message registry, error name = {ERROR}",
168 "RC", rc, "ERROR", name);
Matt Spinler93e29322019-09-20 11:16:15 -0500169
170 throw std::runtime_error("Invalid reason code in message registry");
171 }
172 return reasonCode;
173}
174
175uint8_t getSRCType(const nlohmann::json& src, const std::string& name)
176{
177 // Looks like: "22"
178 std::string srcType = src["Type"];
179 size_t type = strtoul(srcType.c_str(), nullptr, 16);
180 if ((type == 0) || (srcType.size() != 2)) // 1 hex byte
181 {
Matt Spinler4f460312023-07-07 16:24:20 -0500182 lg2::error(
183 "Invalid SRC Type {TYPE} in message registry, error name = {ERROR}",
184 "TYPE", srcType, "ERROR", name);
Matt Spinler93e29322019-09-20 11:16:15 -0500185
186 throw std::runtime_error("Invalid SRC Type in message registry");
187 }
188
189 return type;
190}
191
Matt Spinler3fe93e92023-04-14 14:06:59 -0500192bool getSRCDeconfigFlag(const nlohmann::json& src)
193{
194 return src["DeconfigFlag"].get<bool>();
195}
196
Matt Spinlerda5b76b2023-06-01 15:56:57 -0500197bool getSRCCheckstopFlag(const nlohmann::json& src)
198{
199 return src["CheckstopFlag"].get<bool>();
200}
201
Matt Spinler93e29322019-09-20 11:16:15 -0500202std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
203 getSRCHexwordFields(const nlohmann::json& src, const std::string& name)
204{
205 std::map<SRC::WordNum, SRC::AdditionalDataField> hexwordFields;
206
207 // Build the map of which AdditionalData fields to use for which SRC words
208
209 // Like:
210 // {
211 // "8":
212 // {
213 // "AdditionalDataPropSource": "TEST"
214 // }
215 //
216 // }
217
218 for (const auto& word : src["Words6To9"].items())
219 {
220 std::string num = word.key();
221 size_t wordNum = std::strtoul(num.c_str(), nullptr, 10);
222
223 if (wordNum == 0)
224 {
Matt Spinler4f460312023-07-07 16:24:20 -0500225 lg2::error(
226 "Invalid SRC word number {NUM} in message registry, error name = {ERROR}",
227 "NUM", num, "ERROR", name);
Matt Spinler93e29322019-09-20 11:16:15 -0500228
229 throw std::runtime_error("Invalid SRC word in message registry");
230 }
231
232 auto attributes = word.value();
Zane Shelleye8db29b2021-11-13 10:34:07 -0600233
234 // Use an empty string for the description if it does not exist.
235 auto itr = attributes.find("Description");
236 std::string desc = (attributes.end() != itr) ? *itr : "";
237
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800238 std::tuple<std::string, std::string> adPropSourceDesc(
Zane Shelleye8db29b2021-11-13 10:34:07 -0600239 attributes["AdditionalDataPropSource"], desc);
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800240 hexwordFields[wordNum] = std::move(adPropSourceDesc);
Matt Spinler93e29322019-09-20 11:16:15 -0500241 }
242
243 if (!hexwordFields.empty())
244 {
245 return hexwordFields;
246 }
247
248 return std::nullopt;
249}
250std::optional<std::vector<SRC::WordNum>>
251 getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name)
252{
253 std::vector<SRC::WordNum> symptomIDFields;
254
255 // Looks like:
256 // "SymptomIDFields": ["SRCWord3", "SRCWord6"],
257
Matt Spinler9d59d582021-05-19 07:57:10 -0600258 for (const std::string field : src["SymptomIDFields"])
Matt Spinler93e29322019-09-20 11:16:15 -0500259 {
260 // Just need the last digit off the end, e.g. SRCWord6.
261 // The schema enforces the format of these.
262 auto srcWordNum = field.substr(field.size() - 1);
263 size_t num = std::strtoul(srcWordNum.c_str(), nullptr, 10);
264 if (num == 0)
265 {
Matt Spinler4f460312023-07-07 16:24:20 -0500266 lg2::error(
267 "Invalid symptom ID field {FIELD} in message registry, error name = {ERROR}",
268 "FIELD", field, "ERROR", name);
Matt Spinler93e29322019-09-20 11:16:15 -0500269
270 throw std::runtime_error("Invalid symptom ID in message registry");
271 }
272 symptomIDFields.push_back(num);
273 }
274 if (!symptomIDFields.empty())
275 {
276 return symptomIDFields;
277 }
278
279 return std::nullopt;
280}
281
282uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode,
283 const nlohmann::json& pelEntry, const std::string& name)
284{
285 uint16_t id = 0;
286
287 // If the ComponentID field is there, use that. Otherwise, if it's a
288 // 0xBD BMC error SRC, use the reasoncode.
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500289 if (pelEntry.contains("ComponentID"))
Matt Spinler93e29322019-09-20 11:16:15 -0500290 {
291 std::string componentID = pelEntry["ComponentID"];
292 id = strtoul(componentID.c_str(), nullptr, 16);
293 }
294 else
295 {
296 // On BMC error SRCs (BD), can just get the component ID from
297 // the first byte of the reason code.
298 if (srcType == static_cast<uint8_t>(SRCType::bmcError))
299 {
300 id = reasonCode & 0xFF00;
301 }
302 else
303 {
Matt Spinler4f460312023-07-07 16:24:20 -0500304 lg2::error(
305 "Missing component ID field in message registry, error name = {ERROR}",
306 "ERROR", name);
Matt Spinler93e29322019-09-20 11:16:15 -0500307
308 throw std::runtime_error(
309 "Missing component ID field in message registry");
310 }
311 }
312
313 return id;
314}
315
Matt Spinler6b427cc2020-04-09 09:42:59 -0500316/**
317 * @brief Says if the JSON is the format that contains AdditionalData keys
318 * as in index into them.
319 *
320 * @param[in] json - The highest level callout JSON
321 *
322 * @return bool - If it is the AdditionalData format or not
323 */
324bool calloutUsesAdditionalData(const nlohmann::json& json)
325{
326 return (json.contains("ADName") &&
327 json.contains("CalloutsWithTheirADValues"));
328}
329
330/**
331 * @brief Finds the callouts to use when there is no AdditionalData,
332 * but the system type may be used as a key.
333 *
Arya K Padman15370292024-05-14 01:48:22 -0500334 * A sample calloutList array looks like the following. The System and Systems
335 * key are optional.
336 *
337 * System key - Value of the key will be the system name as a string. The
338 * callouts for a specific system can define under this key.
339 *
340 * Systems key - Value of the key will be an array of system names in the form
341 * of string. The callouts common to the systems mentioned in the array can
342 * define under this key.
343 *
344 * If both System and Systems not present it means that entry applies to every
345 * configuration that doesn't have another entry with a matching System and
346 * Systems key.
Matt Spinler6b427cc2020-04-09 09:42:59 -0500347 *
348 * {
349 * "System": "system1",
350 * "CalloutList":
351 * [
352 * {
353 * "Priority": "high",
354 * "LocCode": "P1-C1"
355 * },
356 * {
357 * "Priority": "low",
358 * "LocCode": "P1"
359 * }
360 * ]
Arya K Padman15370292024-05-14 01:48:22 -0500361 * },
362 * {
363 * "Systems": ["system1", 'system2"],
364 * "CalloutList":
365 * [
366 * {
367 * "Priority": "high",
368 * "LocCode": "P0-C1"
369 * },
370 * {
371 * "Priority": "low",
372 * "LocCode": "P0"
373 * }
374 * ]
Matt Spinler6b427cc2020-04-09 09:42:59 -0500375 * }
Arya K Padman15370292024-05-14 01:48:22 -0500376 *
377 * @param[in] json - The callout JSON
378 * @param[in] systemNames - List of compatible system type names
379 * @param[out] calloutLists - The JSON array which will hold the calloutlist to
380 * use specific to the system.
381 *
382 * @return - Throws runtime exception if json is not an array or if calloutLists
383 * is empty.
Matt Spinler6b427cc2020-04-09 09:42:59 -0500384 */
Arya K Padman15370292024-05-14 01:48:22 -0500385static void findCalloutList(const nlohmann::json& json,
386 const std::vector<std::string>& systemNames,
387 nlohmann::json& calloutLists)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500388{
Matt Spinler6b427cc2020-04-09 09:42:59 -0500389 if (!json.is_array())
390 {
391 throw std::runtime_error{"findCalloutList was not passed a JSON array"};
392 }
393
Arya K Padman15370292024-05-14 01:48:22 -0500394 // Flag to indicate whether system specific callouts found or not
395 bool foundCallouts = false;
396
397 for (const auto& callouts : json)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500398 {
Arya K Padman15370292024-05-14 01:48:22 -0500399 if (callouts.contains("System"))
Matt Spinler6b427cc2020-04-09 09:42:59 -0500400 {
Arya K Padman15370292024-05-14 01:48:22 -0500401 if (std::ranges::find(systemNames,
402 callouts["System"].get<std::string>()) !=
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500403 systemNames.end())
Matt Spinler6b427cc2020-04-09 09:42:59 -0500404 {
Arya K Padman15370292024-05-14 01:48:22 -0500405 calloutLists.insert(calloutLists.end(),
406 callouts["CalloutList"].begin(),
407 callouts["CalloutList"].end());
408 foundCallouts = true;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500409 }
Arya K Padman15370292024-05-14 01:48:22 -0500410 continue;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500411 }
Arya K Padman15370292024-05-14 01:48:22 -0500412
413 if (callouts.contains("Systems"))
Matt Spinler6b427cc2020-04-09 09:42:59 -0500414 {
Arya K Padman15370292024-05-14 01:48:22 -0500415 std::vector<std::string> systems =
416 callouts["Systems"].get<std::vector<std::string>>();
417 auto inSystemNames = [systemNames](const auto& system) {
418 return (std::ranges::find(systemNames, system) !=
419 systemNames.end());
420 };
421 if (std::ranges::any_of(systems, inSystemNames))
422 {
423 calloutLists.insert(calloutLists.end(),
424 callouts["CalloutList"].begin(),
425 callouts["CalloutList"].end());
426 foundCallouts = true;
427 }
428 continue;
429 }
430
431 // Any entry if neither System/Systems key matches with system name
432 if (!foundCallouts)
433 {
434 calloutLists.insert(calloutLists.end(),
435 callouts["CalloutList"].begin(),
436 callouts["CalloutList"].end());
Matt Spinler6b427cc2020-04-09 09:42:59 -0500437 }
438 }
Arya K Padman15370292024-05-14 01:48:22 -0500439 if (calloutLists.empty())
Matt Spinler6b427cc2020-04-09 09:42:59 -0500440 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500441 std::string types;
442 std::for_each(systemNames.begin(), systemNames.end(),
443 [&types](const auto& t) { types += t + '|'; });
Matt Spinler4f460312023-07-07 16:24:20 -0500444 lg2::warning(
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500445 "No matching system name entry or default system name entry "
Matt Spinler4f460312023-07-07 16:24:20 -0500446 " for PEL callout list, names = {TYPES}",
447 "TYPES", types);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500448
449 throw std::runtime_error{
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500450 "Could not find a CalloutList JSON for this error and system name"};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500451 }
Matt Spinler6b427cc2020-04-09 09:42:59 -0500452}
453
454/**
455 * @brief Creates a RegistryCallout based on the input JSON.
456 *
457 * The JSON looks like:
458 * {
459 * "Priority": "high",
460 * "LocCode": "E1"
461 * ...
462 * }
463 *
464 * Schema validation enforces what keys are present.
465 *
466 * @param[in] json - The JSON dictionary entry for a callout
467 *
468 * @return RegistryCallout - A filled in RegistryCallout
469 */
470RegistryCallout makeRegistryCallout(const nlohmann::json& json)
471{
472 RegistryCallout callout;
473
474 callout.priority = "high";
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500475 callout.useInventoryLocCode = false;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500476
477 if (json.contains("Priority"))
478 {
479 callout.priority = json["Priority"].get<std::string>();
480 }
481
482 if (json.contains("LocCode"))
483 {
484 callout.locCode = json["LocCode"].get<std::string>();
485 }
486
487 if (json.contains("Procedure"))
488 {
489 callout.procedure = json["Procedure"].get<std::string>();
490 }
491 else if (json.contains("SymbolicFRU"))
492 {
493 callout.symbolicFRU = json["SymbolicFRU"].get<std::string>();
494 }
495 else if (json.contains("SymbolicFRUTrusted"))
496 {
497 callout.symbolicFRUTrusted =
498 json["SymbolicFRUTrusted"].get<std::string>();
499 }
500
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500501 if (json.contains("UseInventoryLocCode"))
502 {
503 callout.useInventoryLocCode = json["UseInventoryLocCode"].get<bool>();
504 }
505
Matt Spinler6b427cc2020-04-09 09:42:59 -0500506 return callout;
507}
508
509/**
510 * @brief Returns the callouts to use when an AdditionalData key is
511 * required to find the correct entries.
512 *
513 * The System property is used to find which CalloutList to use.
514 * If System is missing, then that CalloutList is valid for
515 * everything.
516 *
517 * The JSON looks like:
Arya K Padman15370292024-05-14 01:48:22 -0500518 * {
519 * "System": "system1",
520 * "CalloutList":
521 * [
522 * {
523 * "Priority": "high",
524 * "LocCode": "P1-C1"
525 * },
526 * {
527 * "Priority": "low",
528 * "LocCode": "P1"
529 * }
530 * ]
531 * },
532 * {
533 * "Systems": ["system1", 'system2"],
534 * "CalloutList":
535 * [
536 * {
537 * "Priority": "high",
538 * "LocCode": "P0-C1"
539 * },
540 * {
541 * "Priority": "low",
542 * "LocCode": "P0"
543 * }
544 * ]
545 * }
Matt Spinler6b427cc2020-04-09 09:42:59 -0500546 *
547 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500548 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500549 *
550 * @return std::vector<RegistryCallout> - The callouts to use
551 */
Patrick Williams075c7922024-08-16 15:19:49 -0400552std::vector<RegistryCallout> getCalloutsWithoutAD(
553 const nlohmann::json& json, const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500554{
555 std::vector<RegistryCallout> calloutEntries;
556
Arya K Padman15370292024-05-14 01:48:22 -0500557 nlohmann::json calloutLists = nlohmann::json::array();
558
Matt Spinler6b427cc2020-04-09 09:42:59 -0500559 // Find the CalloutList to use based on the system type
Arya K Padman15370292024-05-14 01:48:22 -0500560 findCalloutList(json, systemNames, calloutLists);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500561
562 // We finally found the callouts, make the objects.
Arya K Padman15370292024-05-14 01:48:22 -0500563 for (const auto& callout : calloutLists)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500564 {
565 calloutEntries.push_back(std::move(makeRegistryCallout(callout)));
566 }
567
568 return calloutEntries;
569}
570
571/**
572 * @brief Returns the callouts to use when an AdditionalData key is
573 * required to find the correct entries.
574 *
575 * The JSON looks like:
576 * {
577 * "ADName": "PROC_NUM",
578 * "CalloutsWithTheirADValues":
579 * [
580 * {
581 * "ADValue": "0",
582 * "Callouts":
583 * [
584 * {
585 * "CalloutList":
586 * [
587 * {
588 * "Priority": "high",
589 * "LocCode": "P1-C5"
590 * }
591 * ]
592 * }
593 * ]
594 * }
595 * ]
596 * }
597 *
598 * Note that the "Callouts" entry above is the same as the top level
599 * entry used when there is no AdditionalData key.
600 *
601 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500602 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500603 * @param[in] additionalData - The AdditionalData property
604 *
605 * @return std::vector<RegistryCallout> - The callouts to use
606 */
Patrick Williams075c7922024-08-16 15:19:49 -0400607std::vector<RegistryCallout> getCalloutsUsingAD(
608 const nlohmann::json& json, const std::vector<std::string>& systemNames,
609 const AdditionalData& additionalData)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500610{
611 // This indicates which AD field we'll be using
612 auto keyName = json["ADName"].get<std::string>();
613
614 // Get the actual value from the AD data
615 auto adValue = additionalData.getValue(keyName);
616
617 if (!adValue)
618 {
619 // The AdditionalData did not contain the necessary key
Matt Spinler4f460312023-07-07 16:24:20 -0500620 lg2::warning("The PEL message registry callouts JSON "
621 "said to use an AdditionalData key that isn't in the "
622 "AdditionalData event log property, key = {KEY}",
623 "KEY", keyName);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500624 throw std::runtime_error{
625 "Missing AdditionalData entry for this callout"};
626 }
627
628 const auto& callouts = json["CalloutsWithTheirADValues"];
629
630 // find the entry with that AD value
Patrick Williams075c7922024-08-16 15:19:49 -0400631 auto it = std::find_if(
632 callouts.begin(), callouts.end(), [adValue](const nlohmann::json& j) {
633 return *adValue == j["ADValue"].get<std::string>();
634 });
Matt Spinler6b427cc2020-04-09 09:42:59 -0500635
636 if (it == callouts.end())
637 {
Matt Spinlerf397afc2021-01-29 11:21:44 -0600638 // This can happen if not all possible values were in the
Matt Spinler3d923312022-08-01 09:52:55 -0500639 // message registry and that's fine. There may be a
640 // "CalloutsWhenNoADMatch" section that contains callouts
641 // to use in this case.
642 if (json.contains("CalloutsWhenNoADMatch"))
643 {
644 return getCalloutsWithoutAD(json["CalloutsWhenNoADMatch"],
645 systemNames);
646 }
Matt Spinlerf397afc2021-01-29 11:21:44 -0600647 return std::vector<RegistryCallout>{};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500648 }
649
650 // Proceed to find the callouts possibly based on system type.
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500651 return getCalloutsWithoutAD((*it)["Callouts"], systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500652}
653
Matt Spinler711f1122022-12-15 11:41:20 -0600654/**
655 * @brief Returns the journal capture information
656 *
657 * The JSON looks like:
658 * "JournalCapture": {
659 * "NumLines": 30
660 * }
661 *
662 * "JournalCapture":
663 * {
664 * "Sections": [
665 * {
666 * "SyslogID": "phosphor-log-manager",
667 * "NumLines": 20
668 * }
669 * ]
670 * }
671 *
672 * @param json - The journal capture JSON
673 * @return JournalCapture - The filled in variant
674 */
675JournalCapture getJournalCapture(const nlohmann::json& json)
676{
677 JournalCapture capt;
678
679 // Primary key is either NumLines or Sections.
680 if (json.contains("NumLines"))
681 {
682 capt = json.at("NumLines").get<size_t>();
683 }
684 else if (json.contains("Sections"))
685 {
686 AppCaptureList captures;
687 for (const auto& capture : json.at("Sections"))
688 {
689 AppCapture ac;
690 ac.syslogID = capture.at("SyslogID").get<std::string>();
691 ac.numLines = capture.at("NumLines").get<size_t>();
692 captures.push_back(std::move(ac));
693 }
694
695 capt = captures;
696 }
697 else
698 {
Matt Spinler4f460312023-07-07 16:24:20 -0500699 lg2::error("JournalCapture section not the right format");
Matt Spinler711f1122022-12-15 11:41:20 -0600700 throw std::runtime_error{"JournalCapture section not the right format"};
701 }
702
703 return capt;
704}
705
Matt Spinler367144c2019-09-19 15:33:52 -0500706} // namespace helper
707
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800708std::optional<Entry> Registry::lookup(const std::string& name, LookupType type,
709 bool toCache)
Matt Spinler367144c2019-09-19 15:33:52 -0500710{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800711 std::optional<nlohmann::json> registryTmp;
712 auto& registryOpt = (_registry) ? _registry : registryTmp;
713 if (!registryOpt)
Matt Spinler367144c2019-09-19 15:33:52 -0500714 {
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800715 registryOpt = readRegistry(_registryFile);
716 if (!registryOpt)
717 {
718 return std::nullopt;
719 }
720 else if (toCache)
721 {
722 // Save message registry in memory for peltool
723 _registry = std::move(registryTmp);
724 }
Matt Spinler367144c2019-09-19 15:33:52 -0500725 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800726 auto& reg = (_registry) ? _registry : registryTmp;
727 const auto& registry = reg.value();
Matt Spinler367144c2019-09-19 15:33:52 -0500728 // Find an entry with this name in the PEL array.
Patrick Williams075c7922024-08-16 15:19:49 -0400729 auto e = std::find_if(
730 registry["PELs"].begin(), registry["PELs"].end(),
731 [&name, &type](const nlohmann::json& j) {
732 return ((name == j.at("Name").get<std::string>() &&
733 type == LookupType::name) ||
734 (name == j.at("SRC").at("ReasonCode").get<std::string>() &&
735 type == LookupType::reasonCode));
736 });
Matt Spinler367144c2019-09-19 15:33:52 -0500737
738 if (e != registry["PELs"].end())
739 {
740 // Fill in the Entry structure from the JSON. Most, but not all, fields
741 // are optional.
742
743 try
744 {
745 Entry entry;
746 entry.name = (*e)["Name"];
Matt Spinler23970b02022-02-25 16:34:46 -0600747
748 if (e->contains("Subsystem"))
749 {
750 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]);
751 }
Matt Spinlere07f9152019-11-01 10:48:36 -0500752
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500753 if (e->contains("ActionFlags"))
Matt Spinlere07f9152019-11-01 10:48:36 -0500754 {
755 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]);
756 }
Matt Spinler367144c2019-09-19 15:33:52 -0500757
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500758 if (e->contains("MfgActionFlags"))
Matt Spinler367144c2019-09-19 15:33:52 -0500759 {
760 entry.mfgActionFlags =
761 helper::getActionFlags((*e)["MfgActionFlags"]);
762 }
763
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500764 if (e->contains("Severity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500765 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500766 entry.severity = helper::getSeverities((*e)["Severity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500767 }
768
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500769 if (e->contains("MfgSeverity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500770 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500771 entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500772 }
773
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500774 if (e->contains("EventType"))
Matt Spinler367144c2019-09-19 15:33:52 -0500775 {
776 entry.eventType = helper::getEventType((*e)["EventType"]);
777 }
778
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500779 if (e->contains("EventScope"))
Matt Spinler367144c2019-09-19 15:33:52 -0500780 {
781 entry.eventScope = helper::getEventScope((*e)["EventScope"]);
782 }
783
Matt Spinler93e29322019-09-20 11:16:15 -0500784 auto& src = (*e)["SRC"];
785 entry.src.reasonCode = helper::getSRCReasonCode(src, name);
786
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500787 if (src.contains("Type"))
Matt Spinler93e29322019-09-20 11:16:15 -0500788 {
789 entry.src.type = helper::getSRCType(src, name);
790 }
791 else
792 {
793 entry.src.type = static_cast<uint8_t>(SRCType::bmcError);
794 }
795
796 // Now that we know the SRC type and reason code,
797 // we can get the component ID.
798 entry.componentID = helper::getComponentID(
799 entry.src.type, entry.src.reasonCode, *e, name);
800
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500801 if (src.contains("Words6To9"))
Matt Spinler93e29322019-09-20 11:16:15 -0500802 {
Patrick Williams075c7922024-08-16 15:19:49 -0400803 entry.src.hexwordADFields =
804 helper::getSRCHexwordFields(src, name);
Matt Spinler93e29322019-09-20 11:16:15 -0500805 }
806
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500807 if (src.contains("SymptomIDFields"))
Matt Spinler93e29322019-09-20 11:16:15 -0500808 {
809 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
810 }
811
Matt Spinler3fe93e92023-04-14 14:06:59 -0500812 if (src.contains("DeconfigFlag"))
813 {
814 entry.src.deconfigFlag = helper::getSRCDeconfigFlag(src);
815 }
816
Matt Spinlerda5b76b2023-06-01 15:56:57 -0500817 if (src.contains("CheckstopFlag"))
818 {
819 entry.src.checkstopFlag = helper::getSRCCheckstopFlag(src);
820 }
821
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800822 auto& doc = (*e)["Documentation"];
823 entry.doc.message = doc["Message"];
824 entry.doc.description = doc["Description"];
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500825 if (doc.contains("MessageArgSources"))
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800826 {
Matt Spinler3bb15b92022-04-27 09:32:10 -0500827 entry.doc.messageArgSources = doc["MessageArgSources"];
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800828 }
829
Matt Spinlerd8e29002020-04-09 09:11:22 -0500830 // If there are callouts defined, save the JSON for later
831 if (_loadCallouts)
832 {
833 if (e->contains("Callouts"))
834 {
835 entry.callouts = (*e)["Callouts"];
836 }
837 else if (e->contains("CalloutsUsingAD"))
838 {
839 entry.callouts = (*e)["CalloutsUsingAD"];
840 }
841 }
842
Matt Spinler711f1122022-12-15 11:41:20 -0600843 if (e->contains("JournalCapture"))
844 {
845 entry.journalCapture =
846 helper::getJournalCapture((*e)["JournalCapture"]);
847 }
848
Matt Spinler367144c2019-09-19 15:33:52 -0500849 return entry;
850 }
Matt Spinler45796e82022-07-01 11:25:27 -0500851 catch (const std::exception& ex)
Matt Spinler367144c2019-09-19 15:33:52 -0500852 {
Matt Spinler4f460312023-07-07 16:24:20 -0500853 lg2::error("Found invalid message registry field. Error: {ERROR}",
854 "ERROR", ex);
Matt Spinler367144c2019-09-19 15:33:52 -0500855 }
856 }
857
858 return std::nullopt;
859}
860
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800861std::optional<nlohmann::json>
862 Registry::readRegistry(const std::filesystem::path& registryFile)
863{
864 // Look in /etc first in case someone put a test file there
865 fs::path debugFile{fs::path{debugFilePath} / registryFileName};
866 nlohmann::json registry;
867 std::ifstream file;
868
869 if (fs::exists(debugFile))
870 {
Matt Spinler4f460312023-07-07 16:24:20 -0500871 lg2::info("Using debug PEL message registry");
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800872 file.open(debugFile);
873 }
874 else
875 {
876 file.open(registryFile);
877 }
878
879 try
880 {
881 registry = nlohmann::json::parse(file);
882 }
Patrick Williams66491c62021-10-06 12:23:37 -0500883 catch (const std::exception& e)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800884 {
Matt Spinler4f460312023-07-07 16:24:20 -0500885 lg2::error("Error parsing message registry JSON. Error: {ERROR}",
886 "ERROR", e);
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800887 return std::nullopt;
888 }
889 return registry;
890}
891
Matt Spinler6b427cc2020-04-09 09:42:59 -0500892std::vector<RegistryCallout>
893 Registry::getCallouts(const nlohmann::json& calloutJSON,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500894 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500895 const AdditionalData& additionalData)
896{
897 // The JSON may either use an AdditionalData key
898 // as an index, or not.
899 if (helper::calloutUsesAdditionalData(calloutJSON))
900 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500901 return helper::getCalloutsUsingAD(calloutJSON, systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500902 additionalData);
903 }
904
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500905 return helper::getCalloutsWithoutAD(calloutJSON, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500906}
907
Matt Spinler367144c2019-09-19 15:33:52 -0500908} // namespace message
909} // namespace pels
910} // namespace openpower