blob: 4dc1f0b191ae9a964210a8c2e74eacf194d62e9e [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
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}
249std::optional<std::vector<SRC::WordNum>>
250 getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name)
251{
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 *
333 * One entry in the array looks like the following. The System key
334 * is optional and if not present it means that entry applies to
335 * every configuration that doesn't have another entry with a matching
336 * System key.
337 *
338 * {
339 * "System": "system1",
340 * "CalloutList":
341 * [
342 * {
343 * "Priority": "high",
344 * "LocCode": "P1-C1"
345 * },
346 * {
347 * "Priority": "low",
348 * "LocCode": "P1"
349 * }
350 * ]
351 * }
352 */
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500353const nlohmann::json&
354 findCalloutList(const nlohmann::json& json,
355 const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500356{
357 const nlohmann::json* callouts = nullptr;
358
359 if (!json.is_array())
360 {
361 throw std::runtime_error{"findCalloutList was not passed a JSON array"};
362 }
363
364 // The entry with the system type match will take precedence over the entry
365 // without any "System" field in it at all, which will match all other
366 // cases.
367 for (const auto& calloutList : json)
368 {
369 if (calloutList.contains("System"))
370 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500371 if (std::find(systemNames.begin(), systemNames.end(),
372 calloutList["System"].get<std::string>()) !=
373 systemNames.end())
Matt Spinler6b427cc2020-04-09 09:42:59 -0500374 {
375 callouts = &calloutList["CalloutList"];
376 break;
377 }
378 }
379 else
380 {
381 // Any entry with no System key
382 callouts = &calloutList["CalloutList"];
383 }
384 }
385
386 if (!callouts)
387 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500388 std::string types;
389 std::for_each(systemNames.begin(), systemNames.end(),
390 [&types](const auto& t) { types += t + '|'; });
Matt Spinler4f460312023-07-07 16:24:20 -0500391 lg2::warning(
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500392 "No matching system name entry or default system name entry "
Matt Spinler4f460312023-07-07 16:24:20 -0500393 " for PEL callout list, names = {TYPES}",
394 "TYPES", types);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500395
396 throw std::runtime_error{
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500397 "Could not find a CalloutList JSON for this error and system name"};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500398 }
399
400 return *callouts;
401}
402
403/**
404 * @brief Creates a RegistryCallout based on the input JSON.
405 *
406 * The JSON looks like:
407 * {
408 * "Priority": "high",
409 * "LocCode": "E1"
410 * ...
411 * }
412 *
413 * Schema validation enforces what keys are present.
414 *
415 * @param[in] json - The JSON dictionary entry for a callout
416 *
417 * @return RegistryCallout - A filled in RegistryCallout
418 */
419RegistryCallout makeRegistryCallout(const nlohmann::json& json)
420{
421 RegistryCallout callout;
422
423 callout.priority = "high";
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500424 callout.useInventoryLocCode = false;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500425
426 if (json.contains("Priority"))
427 {
428 callout.priority = json["Priority"].get<std::string>();
429 }
430
431 if (json.contains("LocCode"))
432 {
433 callout.locCode = json["LocCode"].get<std::string>();
434 }
435
436 if (json.contains("Procedure"))
437 {
438 callout.procedure = json["Procedure"].get<std::string>();
439 }
440 else if (json.contains("SymbolicFRU"))
441 {
442 callout.symbolicFRU = json["SymbolicFRU"].get<std::string>();
443 }
444 else if (json.contains("SymbolicFRUTrusted"))
445 {
446 callout.symbolicFRUTrusted =
447 json["SymbolicFRUTrusted"].get<std::string>();
448 }
449
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500450 if (json.contains("UseInventoryLocCode"))
451 {
452 callout.useInventoryLocCode = json["UseInventoryLocCode"].get<bool>();
453 }
454
Matt Spinler6b427cc2020-04-09 09:42:59 -0500455 return callout;
456}
457
458/**
459 * @brief Returns the callouts to use when an AdditionalData key is
460 * required to find the correct entries.
461 *
462 * The System property is used to find which CalloutList to use.
463 * If System is missing, then that CalloutList is valid for
464 * everything.
465 *
466 * The JSON looks like:
467 * [
468 * {
469 * "System": "systemA",
470 * "CalloutList":
471 * [
472 * {
473 * "Priority": "high",
474 * "LocCode": "P1-C5"
475 * }
476 * ]
477 * }
478 * ]
479 *
480 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500481 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500482 *
483 * @return std::vector<RegistryCallout> - The callouts to use
484 */
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500485std::vector<RegistryCallout>
486 getCalloutsWithoutAD(const nlohmann::json& json,
487 const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500488{
489 std::vector<RegistryCallout> calloutEntries;
490
491 // Find the CalloutList to use based on the system type
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500492 const auto& calloutList = findCalloutList(json, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500493
494 // We finally found the callouts, make the objects.
495 for (const auto& callout : calloutList)
496 {
497 calloutEntries.push_back(std::move(makeRegistryCallout(callout)));
498 }
499
500 return calloutEntries;
501}
502
503/**
504 * @brief Returns the callouts to use when an AdditionalData key is
505 * required to find the correct entries.
506 *
507 * The JSON looks like:
508 * {
509 * "ADName": "PROC_NUM",
510 * "CalloutsWithTheirADValues":
511 * [
512 * {
513 * "ADValue": "0",
514 * "Callouts":
515 * [
516 * {
517 * "CalloutList":
518 * [
519 * {
520 * "Priority": "high",
521 * "LocCode": "P1-C5"
522 * }
523 * ]
524 * }
525 * ]
526 * }
527 * ]
528 * }
529 *
530 * Note that the "Callouts" entry above is the same as the top level
531 * entry used when there is no AdditionalData key.
532 *
533 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500534 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500535 * @param[in] additionalData - The AdditionalData property
536 *
537 * @return std::vector<RegistryCallout> - The callouts to use
538 */
539std::vector<RegistryCallout>
540 getCalloutsUsingAD(const nlohmann::json& json,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500541 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500542 const AdditionalData& additionalData)
543{
544 // This indicates which AD field we'll be using
545 auto keyName = json["ADName"].get<std::string>();
546
547 // Get the actual value from the AD data
548 auto adValue = additionalData.getValue(keyName);
549
550 if (!adValue)
551 {
552 // The AdditionalData did not contain the necessary key
Matt Spinler4f460312023-07-07 16:24:20 -0500553 lg2::warning("The PEL message registry callouts JSON "
554 "said to use an AdditionalData key that isn't in the "
555 "AdditionalData event log property, key = {KEY}",
556 "KEY", keyName);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500557 throw std::runtime_error{
558 "Missing AdditionalData entry for this callout"};
559 }
560
561 const auto& callouts = json["CalloutsWithTheirADValues"];
562
563 // find the entry with that AD value
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500564 auto it = std::find_if(callouts.begin(), callouts.end(),
565 [adValue](const nlohmann::json& j) {
566 return *adValue == j["ADValue"].get<std::string>();
567 });
Matt Spinler6b427cc2020-04-09 09:42:59 -0500568
569 if (it == callouts.end())
570 {
Matt Spinlerf397afc2021-01-29 11:21:44 -0600571 // This can happen if not all possible values were in the
Matt Spinler3d923312022-08-01 09:52:55 -0500572 // message registry and that's fine. There may be a
573 // "CalloutsWhenNoADMatch" section that contains callouts
574 // to use in this case.
575 if (json.contains("CalloutsWhenNoADMatch"))
576 {
577 return getCalloutsWithoutAD(json["CalloutsWhenNoADMatch"],
578 systemNames);
579 }
Matt Spinlerf397afc2021-01-29 11:21:44 -0600580 return std::vector<RegistryCallout>{};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500581 }
582
583 // Proceed to find the callouts possibly based on system type.
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500584 return getCalloutsWithoutAD((*it)["Callouts"], systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500585}
586
Matt Spinler711f1122022-12-15 11:41:20 -0600587/**
588 * @brief Returns the journal capture information
589 *
590 * The JSON looks like:
591 * "JournalCapture": {
592 * "NumLines": 30
593 * }
594 *
595 * "JournalCapture":
596 * {
597 * "Sections": [
598 * {
599 * "SyslogID": "phosphor-log-manager",
600 * "NumLines": 20
601 * }
602 * ]
603 * }
604 *
605 * @param json - The journal capture JSON
606 * @return JournalCapture - The filled in variant
607 */
608JournalCapture getJournalCapture(const nlohmann::json& json)
609{
610 JournalCapture capt;
611
612 // Primary key is either NumLines or Sections.
613 if (json.contains("NumLines"))
614 {
615 capt = json.at("NumLines").get<size_t>();
616 }
617 else if (json.contains("Sections"))
618 {
619 AppCaptureList captures;
620 for (const auto& capture : json.at("Sections"))
621 {
622 AppCapture ac;
623 ac.syslogID = capture.at("SyslogID").get<std::string>();
624 ac.numLines = capture.at("NumLines").get<size_t>();
625 captures.push_back(std::move(ac));
626 }
627
628 capt = captures;
629 }
630 else
631 {
Matt Spinler4f460312023-07-07 16:24:20 -0500632 lg2::error("JournalCapture section not the right format");
Matt Spinler711f1122022-12-15 11:41:20 -0600633 throw std::runtime_error{"JournalCapture section not the right format"};
634 }
635
636 return capt;
637}
638
Matt Spinler367144c2019-09-19 15:33:52 -0500639} // namespace helper
640
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800641std::optional<Entry> Registry::lookup(const std::string& name, LookupType type,
642 bool toCache)
Matt Spinler367144c2019-09-19 15:33:52 -0500643{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800644 std::optional<nlohmann::json> registryTmp;
645 auto& registryOpt = (_registry) ? _registry : registryTmp;
646 if (!registryOpt)
Matt Spinler367144c2019-09-19 15:33:52 -0500647 {
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800648 registryOpt = readRegistry(_registryFile);
649 if (!registryOpt)
650 {
651 return std::nullopt;
652 }
653 else if (toCache)
654 {
655 // Save message registry in memory for peltool
656 _registry = std::move(registryTmp);
657 }
Matt Spinler367144c2019-09-19 15:33:52 -0500658 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800659 auto& reg = (_registry) ? _registry : registryTmp;
660 const auto& registry = reg.value();
Matt Spinler367144c2019-09-19 15:33:52 -0500661 // Find an entry with this name in the PEL array.
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500662 auto e = std::find_if(registry["PELs"].begin(), registry["PELs"].end(),
Matt Spinler81bc5612023-06-01 16:48:19 -0500663 [&name, &type](const nlohmann::json& j) {
664 return ((name == j.at("Name").get<std::string>() &&
665 type == LookupType::name) ||
666 (name == j.at("SRC").at("ReasonCode").get<std::string>() &&
667 type == LookupType::reasonCode));
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500668 });
Matt Spinler367144c2019-09-19 15:33:52 -0500669
670 if (e != registry["PELs"].end())
671 {
672 // Fill in the Entry structure from the JSON. Most, but not all, fields
673 // are optional.
674
675 try
676 {
677 Entry entry;
678 entry.name = (*e)["Name"];
Matt Spinler23970b02022-02-25 16:34:46 -0600679
680 if (e->contains("Subsystem"))
681 {
682 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]);
683 }
Matt Spinlere07f9152019-11-01 10:48:36 -0500684
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500685 if (e->contains("ActionFlags"))
Matt Spinlere07f9152019-11-01 10:48:36 -0500686 {
687 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]);
688 }
Matt Spinler367144c2019-09-19 15:33:52 -0500689
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500690 if (e->contains("MfgActionFlags"))
Matt Spinler367144c2019-09-19 15:33:52 -0500691 {
692 entry.mfgActionFlags =
693 helper::getActionFlags((*e)["MfgActionFlags"]);
694 }
695
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500696 if (e->contains("Severity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500697 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500698 entry.severity = helper::getSeverities((*e)["Severity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500699 }
700
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500701 if (e->contains("MfgSeverity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500702 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500703 entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500704 }
705
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500706 if (e->contains("EventType"))
Matt Spinler367144c2019-09-19 15:33:52 -0500707 {
708 entry.eventType = helper::getEventType((*e)["EventType"]);
709 }
710
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500711 if (e->contains("EventScope"))
Matt Spinler367144c2019-09-19 15:33:52 -0500712 {
713 entry.eventScope = helper::getEventScope((*e)["EventScope"]);
714 }
715
Matt Spinler93e29322019-09-20 11:16:15 -0500716 auto& src = (*e)["SRC"];
717 entry.src.reasonCode = helper::getSRCReasonCode(src, name);
718
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500719 if (src.contains("Type"))
Matt Spinler93e29322019-09-20 11:16:15 -0500720 {
721 entry.src.type = helper::getSRCType(src, name);
722 }
723 else
724 {
725 entry.src.type = static_cast<uint8_t>(SRCType::bmcError);
726 }
727
728 // Now that we know the SRC type and reason code,
729 // we can get the component ID.
730 entry.componentID = helper::getComponentID(
731 entry.src.type, entry.src.reasonCode, *e, name);
732
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500733 if (src.contains("Words6To9"))
Matt Spinler93e29322019-09-20 11:16:15 -0500734 {
Patrick Williams2544b412022-10-04 08:41:06 -0500735 entry.src.hexwordADFields = helper::getSRCHexwordFields(src,
736 name);
Matt Spinler93e29322019-09-20 11:16:15 -0500737 }
738
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500739 if (src.contains("SymptomIDFields"))
Matt Spinler93e29322019-09-20 11:16:15 -0500740 {
741 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
742 }
743
Matt Spinler3fe93e92023-04-14 14:06:59 -0500744 if (src.contains("DeconfigFlag"))
745 {
746 entry.src.deconfigFlag = helper::getSRCDeconfigFlag(src);
747 }
748
Matt Spinlerda5b76b2023-06-01 15:56:57 -0500749 if (src.contains("CheckstopFlag"))
750 {
751 entry.src.checkstopFlag = helper::getSRCCheckstopFlag(src);
752 }
753
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800754 auto& doc = (*e)["Documentation"];
755 entry.doc.message = doc["Message"];
756 entry.doc.description = doc["Description"];
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500757 if (doc.contains("MessageArgSources"))
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800758 {
Matt Spinler3bb15b92022-04-27 09:32:10 -0500759 entry.doc.messageArgSources = doc["MessageArgSources"];
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800760 }
761
Matt Spinlerd8e29002020-04-09 09:11:22 -0500762 // If there are callouts defined, save the JSON for later
763 if (_loadCallouts)
764 {
765 if (e->contains("Callouts"))
766 {
767 entry.callouts = (*e)["Callouts"];
768 }
769 else if (e->contains("CalloutsUsingAD"))
770 {
771 entry.callouts = (*e)["CalloutsUsingAD"];
772 }
773 }
774
Matt Spinler711f1122022-12-15 11:41:20 -0600775 if (e->contains("JournalCapture"))
776 {
777 entry.journalCapture =
778 helper::getJournalCapture((*e)["JournalCapture"]);
779 }
780
Matt Spinler367144c2019-09-19 15:33:52 -0500781 return entry;
782 }
Matt Spinler45796e82022-07-01 11:25:27 -0500783 catch (const std::exception& ex)
Matt Spinler367144c2019-09-19 15:33:52 -0500784 {
Matt Spinler4f460312023-07-07 16:24:20 -0500785 lg2::error("Found invalid message registry field. Error: {ERROR}",
786 "ERROR", ex);
Matt Spinler367144c2019-09-19 15:33:52 -0500787 }
788 }
789
790 return std::nullopt;
791}
792
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800793std::optional<nlohmann::json>
794 Registry::readRegistry(const std::filesystem::path& registryFile)
795{
796 // Look in /etc first in case someone put a test file there
797 fs::path debugFile{fs::path{debugFilePath} / registryFileName};
798 nlohmann::json registry;
799 std::ifstream file;
800
801 if (fs::exists(debugFile))
802 {
Matt Spinler4f460312023-07-07 16:24:20 -0500803 lg2::info("Using debug PEL message registry");
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800804 file.open(debugFile);
805 }
806 else
807 {
808 file.open(registryFile);
809 }
810
811 try
812 {
813 registry = nlohmann::json::parse(file);
814 }
Patrick Williams66491c62021-10-06 12:23:37 -0500815 catch (const std::exception& e)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800816 {
Matt Spinler4f460312023-07-07 16:24:20 -0500817 lg2::error("Error parsing message registry JSON. Error: {ERROR}",
818 "ERROR", e);
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800819 return std::nullopt;
820 }
821 return registry;
822}
823
Matt Spinler6b427cc2020-04-09 09:42:59 -0500824std::vector<RegistryCallout>
825 Registry::getCallouts(const nlohmann::json& calloutJSON,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500826 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500827 const AdditionalData& additionalData)
828{
829 // The JSON may either use an AdditionalData key
830 // as an index, or not.
831 if (helper::calloutUsesAdditionalData(calloutJSON))
832 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500833 return helper::getCalloutsUsingAD(calloutJSON, systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500834 additionalData);
835 }
836
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500837 return helper::getCalloutsWithoutAD(calloutJSON, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500838}
839
Matt Spinler367144c2019-09-19 15:33:52 -0500840} // namespace message
841} // namespace pels
842} // namespace openpower