blob: 05b286aeb7e0cc08e009d82476f30c748abee315 [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 */
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500552std::vector<RegistryCallout>
553 getCalloutsWithoutAD(const nlohmann::json& json,
554 const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500555{
556 std::vector<RegistryCallout> calloutEntries;
557
Arya K Padman15370292024-05-14 01:48:22 -0500558 nlohmann::json calloutLists = nlohmann::json::array();
559
Matt Spinler6b427cc2020-04-09 09:42:59 -0500560 // Find the CalloutList to use based on the system type
Arya K Padman15370292024-05-14 01:48:22 -0500561 findCalloutList(json, systemNames, calloutLists);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500562
563 // We finally found the callouts, make the objects.
Arya K Padman15370292024-05-14 01:48:22 -0500564 for (const auto& callout : calloutLists)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500565 {
566 calloutEntries.push_back(std::move(makeRegistryCallout(callout)));
567 }
568
569 return calloutEntries;
570}
571
572/**
573 * @brief Returns the callouts to use when an AdditionalData key is
574 * required to find the correct entries.
575 *
576 * The JSON looks like:
577 * {
578 * "ADName": "PROC_NUM",
579 * "CalloutsWithTheirADValues":
580 * [
581 * {
582 * "ADValue": "0",
583 * "Callouts":
584 * [
585 * {
586 * "CalloutList":
587 * [
588 * {
589 * "Priority": "high",
590 * "LocCode": "P1-C5"
591 * }
592 * ]
593 * }
594 * ]
595 * }
596 * ]
597 * }
598 *
599 * Note that the "Callouts" entry above is the same as the top level
600 * entry used when there is no AdditionalData key.
601 *
602 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500603 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500604 * @param[in] additionalData - The AdditionalData property
605 *
606 * @return std::vector<RegistryCallout> - The callouts to use
607 */
608std::vector<RegistryCallout>
609 getCalloutsUsingAD(const nlohmann::json& json,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500610 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500611 const AdditionalData& additionalData)
612{
613 // This indicates which AD field we'll be using
614 auto keyName = json["ADName"].get<std::string>();
615
616 // Get the actual value from the AD data
617 auto adValue = additionalData.getValue(keyName);
618
619 if (!adValue)
620 {
621 // The AdditionalData did not contain the necessary key
Matt Spinler4f460312023-07-07 16:24:20 -0500622 lg2::warning("The PEL message registry callouts JSON "
623 "said to use an AdditionalData key that isn't in the "
624 "AdditionalData event log property, key = {KEY}",
625 "KEY", keyName);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500626 throw std::runtime_error{
627 "Missing AdditionalData entry for this callout"};
628 }
629
630 const auto& callouts = json["CalloutsWithTheirADValues"];
631
632 // find the entry with that AD value
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500633 auto it = std::find_if(callouts.begin(), callouts.end(),
634 [adValue](const nlohmann::json& j) {
635 return *adValue == j["ADValue"].get<std::string>();
636 });
Matt Spinler6b427cc2020-04-09 09:42:59 -0500637
638 if (it == callouts.end())
639 {
Matt Spinlerf397afc2021-01-29 11:21:44 -0600640 // This can happen if not all possible values were in the
Matt Spinler3d923312022-08-01 09:52:55 -0500641 // message registry and that's fine. There may be a
642 // "CalloutsWhenNoADMatch" section that contains callouts
643 // to use in this case.
644 if (json.contains("CalloutsWhenNoADMatch"))
645 {
646 return getCalloutsWithoutAD(json["CalloutsWhenNoADMatch"],
647 systemNames);
648 }
Matt Spinlerf397afc2021-01-29 11:21:44 -0600649 return std::vector<RegistryCallout>{};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500650 }
651
652 // Proceed to find the callouts possibly based on system type.
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500653 return getCalloutsWithoutAD((*it)["Callouts"], systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500654}
655
Matt Spinler711f1122022-12-15 11:41:20 -0600656/**
657 * @brief Returns the journal capture information
658 *
659 * The JSON looks like:
660 * "JournalCapture": {
661 * "NumLines": 30
662 * }
663 *
664 * "JournalCapture":
665 * {
666 * "Sections": [
667 * {
668 * "SyslogID": "phosphor-log-manager",
669 * "NumLines": 20
670 * }
671 * ]
672 * }
673 *
674 * @param json - The journal capture JSON
675 * @return JournalCapture - The filled in variant
676 */
677JournalCapture getJournalCapture(const nlohmann::json& json)
678{
679 JournalCapture capt;
680
681 // Primary key is either NumLines or Sections.
682 if (json.contains("NumLines"))
683 {
684 capt = json.at("NumLines").get<size_t>();
685 }
686 else if (json.contains("Sections"))
687 {
688 AppCaptureList captures;
689 for (const auto& capture : json.at("Sections"))
690 {
691 AppCapture ac;
692 ac.syslogID = capture.at("SyslogID").get<std::string>();
693 ac.numLines = capture.at("NumLines").get<size_t>();
694 captures.push_back(std::move(ac));
695 }
696
697 capt = captures;
698 }
699 else
700 {
Matt Spinler4f460312023-07-07 16:24:20 -0500701 lg2::error("JournalCapture section not the right format");
Matt Spinler711f1122022-12-15 11:41:20 -0600702 throw std::runtime_error{"JournalCapture section not the right format"};
703 }
704
705 return capt;
706}
707
Matt Spinler367144c2019-09-19 15:33:52 -0500708} // namespace helper
709
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800710std::optional<Entry> Registry::lookup(const std::string& name, LookupType type,
711 bool toCache)
Matt Spinler367144c2019-09-19 15:33:52 -0500712{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800713 std::optional<nlohmann::json> registryTmp;
714 auto& registryOpt = (_registry) ? _registry : registryTmp;
715 if (!registryOpt)
Matt Spinler367144c2019-09-19 15:33:52 -0500716 {
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800717 registryOpt = readRegistry(_registryFile);
718 if (!registryOpt)
719 {
720 return std::nullopt;
721 }
722 else if (toCache)
723 {
724 // Save message registry in memory for peltool
725 _registry = std::move(registryTmp);
726 }
Matt Spinler367144c2019-09-19 15:33:52 -0500727 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800728 auto& reg = (_registry) ? _registry : registryTmp;
729 const auto& registry = reg.value();
Matt Spinler367144c2019-09-19 15:33:52 -0500730 // Find an entry with this name in the PEL array.
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500731 auto e = std::find_if(registry["PELs"].begin(), registry["PELs"].end(),
Matt Spinler81bc5612023-06-01 16:48:19 -0500732 [&name, &type](const nlohmann::json& j) {
733 return ((name == j.at("Name").get<std::string>() &&
734 type == LookupType::name) ||
735 (name == j.at("SRC").at("ReasonCode").get<std::string>() &&
736 type == LookupType::reasonCode));
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500737 });
Matt Spinler367144c2019-09-19 15:33:52 -0500738
739 if (e != registry["PELs"].end())
740 {
741 // Fill in the Entry structure from the JSON. Most, but not all, fields
742 // are optional.
743
744 try
745 {
746 Entry entry;
747 entry.name = (*e)["Name"];
Matt Spinler23970b02022-02-25 16:34:46 -0600748
749 if (e->contains("Subsystem"))
750 {
751 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]);
752 }
Matt Spinlere07f9152019-11-01 10:48:36 -0500753
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500754 if (e->contains("ActionFlags"))
Matt Spinlere07f9152019-11-01 10:48:36 -0500755 {
756 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]);
757 }
Matt Spinler367144c2019-09-19 15:33:52 -0500758
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500759 if (e->contains("MfgActionFlags"))
Matt Spinler367144c2019-09-19 15:33:52 -0500760 {
761 entry.mfgActionFlags =
762 helper::getActionFlags((*e)["MfgActionFlags"]);
763 }
764
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500765 if (e->contains("Severity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500766 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500767 entry.severity = helper::getSeverities((*e)["Severity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500768 }
769
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500770 if (e->contains("MfgSeverity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500771 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500772 entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500773 }
774
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500775 if (e->contains("EventType"))
Matt Spinler367144c2019-09-19 15:33:52 -0500776 {
777 entry.eventType = helper::getEventType((*e)["EventType"]);
778 }
779
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500780 if (e->contains("EventScope"))
Matt Spinler367144c2019-09-19 15:33:52 -0500781 {
782 entry.eventScope = helper::getEventScope((*e)["EventScope"]);
783 }
784
Matt Spinler93e29322019-09-20 11:16:15 -0500785 auto& src = (*e)["SRC"];
786 entry.src.reasonCode = helper::getSRCReasonCode(src, name);
787
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500788 if (src.contains("Type"))
Matt Spinler93e29322019-09-20 11:16:15 -0500789 {
790 entry.src.type = helper::getSRCType(src, name);
791 }
792 else
793 {
794 entry.src.type = static_cast<uint8_t>(SRCType::bmcError);
795 }
796
797 // Now that we know the SRC type and reason code,
798 // we can get the component ID.
799 entry.componentID = helper::getComponentID(
800 entry.src.type, entry.src.reasonCode, *e, name);
801
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500802 if (src.contains("Words6To9"))
Matt Spinler93e29322019-09-20 11:16:15 -0500803 {
Patrick Williams2544b412022-10-04 08:41:06 -0500804 entry.src.hexwordADFields = helper::getSRCHexwordFields(src,
805 name);
Matt Spinler93e29322019-09-20 11:16:15 -0500806 }
807
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500808 if (src.contains("SymptomIDFields"))
Matt Spinler93e29322019-09-20 11:16:15 -0500809 {
810 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
811 }
812
Matt Spinler3fe93e92023-04-14 14:06:59 -0500813 if (src.contains("DeconfigFlag"))
814 {
815 entry.src.deconfigFlag = helper::getSRCDeconfigFlag(src);
816 }
817
Matt Spinlerda5b76b2023-06-01 15:56:57 -0500818 if (src.contains("CheckstopFlag"))
819 {
820 entry.src.checkstopFlag = helper::getSRCCheckstopFlag(src);
821 }
822
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800823 auto& doc = (*e)["Documentation"];
824 entry.doc.message = doc["Message"];
825 entry.doc.description = doc["Description"];
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500826 if (doc.contains("MessageArgSources"))
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800827 {
Matt Spinler3bb15b92022-04-27 09:32:10 -0500828 entry.doc.messageArgSources = doc["MessageArgSources"];
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800829 }
830
Matt Spinlerd8e29002020-04-09 09:11:22 -0500831 // If there are callouts defined, save the JSON for later
832 if (_loadCallouts)
833 {
834 if (e->contains("Callouts"))
835 {
836 entry.callouts = (*e)["Callouts"];
837 }
838 else if (e->contains("CalloutsUsingAD"))
839 {
840 entry.callouts = (*e)["CalloutsUsingAD"];
841 }
842 }
843
Matt Spinler711f1122022-12-15 11:41:20 -0600844 if (e->contains("JournalCapture"))
845 {
846 entry.journalCapture =
847 helper::getJournalCapture((*e)["JournalCapture"]);
848 }
849
Matt Spinler367144c2019-09-19 15:33:52 -0500850 return entry;
851 }
Matt Spinler45796e82022-07-01 11:25:27 -0500852 catch (const std::exception& ex)
Matt Spinler367144c2019-09-19 15:33:52 -0500853 {
Matt Spinler4f460312023-07-07 16:24:20 -0500854 lg2::error("Found invalid message registry field. Error: {ERROR}",
855 "ERROR", ex);
Matt Spinler367144c2019-09-19 15:33:52 -0500856 }
857 }
858
859 return std::nullopt;
860}
861
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800862std::optional<nlohmann::json>
863 Registry::readRegistry(const std::filesystem::path& registryFile)
864{
865 // Look in /etc first in case someone put a test file there
866 fs::path debugFile{fs::path{debugFilePath} / registryFileName};
867 nlohmann::json registry;
868 std::ifstream file;
869
870 if (fs::exists(debugFile))
871 {
Matt Spinler4f460312023-07-07 16:24:20 -0500872 lg2::info("Using debug PEL message registry");
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800873 file.open(debugFile);
874 }
875 else
876 {
877 file.open(registryFile);
878 }
879
880 try
881 {
882 registry = nlohmann::json::parse(file);
883 }
Patrick Williams66491c62021-10-06 12:23:37 -0500884 catch (const std::exception& e)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800885 {
Matt Spinler4f460312023-07-07 16:24:20 -0500886 lg2::error("Error parsing message registry JSON. Error: {ERROR}",
887 "ERROR", e);
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800888 return std::nullopt;
889 }
890 return registry;
891}
892
Matt Spinler6b427cc2020-04-09 09:42:59 -0500893std::vector<RegistryCallout>
894 Registry::getCallouts(const nlohmann::json& calloutJSON,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500895 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500896 const AdditionalData& additionalData)
897{
898 // The JSON may either use an AdditionalData key
899 // as an index, or not.
900 if (helper::calloutUsesAdditionalData(calloutJSON))
901 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500902 return helper::getCalloutsUsingAD(calloutJSON, systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500903 additionalData);
904 }
905
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500906 return helper::getCalloutsWithoutAD(calloutJSON, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500907}
908
Matt Spinler367144c2019-09-19 15:33:52 -0500909} // namespace message
910} // namespace pels
911} // namespace openpower