blob: 9cccb0bfd10e2de0ae13570aa30a13bd2efade8d [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 Spinler367144c2019-09-19 15:33:52 -050022#include <phosphor-logging/log.hpp>
23
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;
35using namespace phosphor::logging;
36
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.
49 log<level::ERR>("Invalid subsystem name used in message registry",
50 entry("SUBSYSTEM=%s", subsystemName.c_str()));
51
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.
64 log<level::ERR>("Invalid severity name used in message registry",
65 entry("SEVERITY=%s", severityName.c_str()));
66
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.
119 log<level::ERR>("Invalid action flag name used in message registry",
120 entry("FLAG=%s", flag.c_str()));
121
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 {
137 log<level::ERR>("Invalid event type used in message registry",
138 entry("EVENT_TYPE=%s", eventTypeName.c_str()));
139
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 {
150 log<level::ERR>("Invalid event scope used in registry",
151 entry("EVENT_SCOPE=%s", eventScopeName.c_str()));
152
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 {
165 log<phosphor::logging::level::ERR>(
166 "Invalid reason code in message registry",
167 entry("ERROR_NAME=%s", name.c_str()),
168 entry("REASON_CODE=%s", rc.c_str()));
169
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 {
182 log<phosphor::logging::level::ERR>(
183 "Invalid SRC Type in message registry",
184 entry("ERROR_NAME=%s", name.c_str()),
185 entry("SRC_TYPE=%s", srcType.c_str()));
186
187 throw std::runtime_error("Invalid SRC Type in message registry");
188 }
189
190 return type;
191}
192
193std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
194 getSRCHexwordFields(const nlohmann::json& src, const std::string& name)
195{
196 std::map<SRC::WordNum, SRC::AdditionalDataField> hexwordFields;
197
198 // Build the map of which AdditionalData fields to use for which SRC words
199
200 // Like:
201 // {
202 // "8":
203 // {
204 // "AdditionalDataPropSource": "TEST"
205 // }
206 //
207 // }
208
209 for (const auto& word : src["Words6To9"].items())
210 {
211 std::string num = word.key();
212 size_t wordNum = std::strtoul(num.c_str(), nullptr, 10);
213
214 if (wordNum == 0)
215 {
216 log<phosphor::logging::level::ERR>(
217 "Invalid SRC word number in message registry",
218 entry("ERROR_NAME=%s", name.c_str()),
219 entry("SRC_WORD_NUM=%s", num.c_str()));
220
221 throw std::runtime_error("Invalid SRC word in message registry");
222 }
223
224 auto attributes = word.value();
Zane Shelleye8db29b2021-11-13 10:34:07 -0600225
226 // Use an empty string for the description if it does not exist.
227 auto itr = attributes.find("Description");
228 std::string desc = (attributes.end() != itr) ? *itr : "";
229
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800230 std::tuple<std::string, std::string> adPropSourceDesc(
Zane Shelleye8db29b2021-11-13 10:34:07 -0600231 attributes["AdditionalDataPropSource"], desc);
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800232 hexwordFields[wordNum] = std::move(adPropSourceDesc);
Matt Spinler93e29322019-09-20 11:16:15 -0500233 }
234
235 if (!hexwordFields.empty())
236 {
237 return hexwordFields;
238 }
239
240 return std::nullopt;
241}
242std::optional<std::vector<SRC::WordNum>>
243 getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name)
244{
245 std::vector<SRC::WordNum> symptomIDFields;
246
247 // Looks like:
248 // "SymptomIDFields": ["SRCWord3", "SRCWord6"],
249
Matt Spinler9d59d582021-05-19 07:57:10 -0600250 for (const std::string field : src["SymptomIDFields"])
Matt Spinler93e29322019-09-20 11:16:15 -0500251 {
252 // Just need the last digit off the end, e.g. SRCWord6.
253 // The schema enforces the format of these.
254 auto srcWordNum = field.substr(field.size() - 1);
255 size_t num = std::strtoul(srcWordNum.c_str(), nullptr, 10);
256 if (num == 0)
257 {
258 log<phosphor::logging::level::ERR>(
259 "Invalid symptom ID field in message registry",
260 entry("ERROR_NAME=%s", name.c_str()),
261 entry("FIELD_NAME=%s", srcWordNum.c_str()));
262
263 throw std::runtime_error("Invalid symptom ID in message registry");
264 }
265 symptomIDFields.push_back(num);
266 }
267 if (!symptomIDFields.empty())
268 {
269 return symptomIDFields;
270 }
271
272 return std::nullopt;
273}
274
275uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode,
276 const nlohmann::json& pelEntry, const std::string& name)
277{
278 uint16_t id = 0;
279
280 // If the ComponentID field is there, use that. Otherwise, if it's a
281 // 0xBD BMC error SRC, use the reasoncode.
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500282 if (pelEntry.contains("ComponentID"))
Matt Spinler93e29322019-09-20 11:16:15 -0500283 {
284 std::string componentID = pelEntry["ComponentID"];
285 id = strtoul(componentID.c_str(), nullptr, 16);
286 }
287 else
288 {
289 // On BMC error SRCs (BD), can just get the component ID from
290 // the first byte of the reason code.
291 if (srcType == static_cast<uint8_t>(SRCType::bmcError))
292 {
293 id = reasonCode & 0xFF00;
294 }
295 else
296 {
297 log<level::ERR>("Missing component ID field in message registry",
298 entry("ERROR_NAME=%s", name.c_str()));
299
300 throw std::runtime_error(
301 "Missing component ID field in message registry");
302 }
303 }
304
305 return id;
306}
307
Matt Spinler6b427cc2020-04-09 09:42:59 -0500308/**
309 * @brief Says if the JSON is the format that contains AdditionalData keys
310 * as in index into them.
311 *
312 * @param[in] json - The highest level callout JSON
313 *
314 * @return bool - If it is the AdditionalData format or not
315 */
316bool calloutUsesAdditionalData(const nlohmann::json& json)
317{
318 return (json.contains("ADName") &&
319 json.contains("CalloutsWithTheirADValues"));
320}
321
322/**
323 * @brief Finds the callouts to use when there is no AdditionalData,
324 * but the system type may be used as a key.
325 *
326 * One entry in the array looks like the following. The System key
327 * is optional and if not present it means that entry applies to
328 * every configuration that doesn't have another entry with a matching
329 * System key.
330 *
331 * {
332 * "System": "system1",
333 * "CalloutList":
334 * [
335 * {
336 * "Priority": "high",
337 * "LocCode": "P1-C1"
338 * },
339 * {
340 * "Priority": "low",
341 * "LocCode": "P1"
342 * }
343 * ]
344 * }
345 */
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500346const nlohmann::json&
347 findCalloutList(const nlohmann::json& json,
348 const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500349{
350 const nlohmann::json* callouts = nullptr;
351
352 if (!json.is_array())
353 {
354 throw std::runtime_error{"findCalloutList was not passed a JSON array"};
355 }
356
357 // The entry with the system type match will take precedence over the entry
358 // without any "System" field in it at all, which will match all other
359 // cases.
360 for (const auto& calloutList : json)
361 {
362 if (calloutList.contains("System"))
363 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500364 if (std::find(systemNames.begin(), systemNames.end(),
365 calloutList["System"].get<std::string>()) !=
366 systemNames.end())
Matt Spinler6b427cc2020-04-09 09:42:59 -0500367 {
368 callouts = &calloutList["CalloutList"];
369 break;
370 }
371 }
372 else
373 {
374 // Any entry with no System key
375 callouts = &calloutList["CalloutList"];
376 }
377 }
378
379 if (!callouts)
380 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500381 std::string types;
382 std::for_each(systemNames.begin(), systemNames.end(),
383 [&types](const auto& t) { types += t + '|'; });
Matt Spinler6b427cc2020-04-09 09:42:59 -0500384 log<level::WARNING>(
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500385 "No matching system name entry or default system name entry "
Matt Spinler6b427cc2020-04-09 09:42:59 -0500386 " for PEL callout list",
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500387 entry("SYSTEMNAMES=%s", types.c_str()));
Matt Spinler6b427cc2020-04-09 09:42:59 -0500388
389 throw std::runtime_error{
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500390 "Could not find a CalloutList JSON for this error and system name"};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500391 }
392
393 return *callouts;
394}
395
396/**
397 * @brief Creates a RegistryCallout based on the input JSON.
398 *
399 * The JSON looks like:
400 * {
401 * "Priority": "high",
402 * "LocCode": "E1"
403 * ...
404 * }
405 *
406 * Schema validation enforces what keys are present.
407 *
408 * @param[in] json - The JSON dictionary entry for a callout
409 *
410 * @return RegistryCallout - A filled in RegistryCallout
411 */
412RegistryCallout makeRegistryCallout(const nlohmann::json& json)
413{
414 RegistryCallout callout;
415
416 callout.priority = "high";
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500417 callout.useInventoryLocCode = false;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500418
419 if (json.contains("Priority"))
420 {
421 callout.priority = json["Priority"].get<std::string>();
422 }
423
424 if (json.contains("LocCode"))
425 {
426 callout.locCode = json["LocCode"].get<std::string>();
427 }
428
429 if (json.contains("Procedure"))
430 {
431 callout.procedure = json["Procedure"].get<std::string>();
432 }
433 else if (json.contains("SymbolicFRU"))
434 {
435 callout.symbolicFRU = json["SymbolicFRU"].get<std::string>();
436 }
437 else if (json.contains("SymbolicFRUTrusted"))
438 {
439 callout.symbolicFRUTrusted =
440 json["SymbolicFRUTrusted"].get<std::string>();
441 }
442
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500443 if (json.contains("UseInventoryLocCode"))
444 {
445 callout.useInventoryLocCode = json["UseInventoryLocCode"].get<bool>();
446 }
447
Matt Spinler6b427cc2020-04-09 09:42:59 -0500448 return callout;
449}
450
451/**
452 * @brief Returns the callouts to use when an AdditionalData key is
453 * required to find the correct entries.
454 *
455 * The System property is used to find which CalloutList to use.
456 * If System is missing, then that CalloutList is valid for
457 * everything.
458 *
459 * The JSON looks like:
460 * [
461 * {
462 * "System": "systemA",
463 * "CalloutList":
464 * [
465 * {
466 * "Priority": "high",
467 * "LocCode": "P1-C5"
468 * }
469 * ]
470 * }
471 * ]
472 *
473 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500474 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500475 *
476 * @return std::vector<RegistryCallout> - The callouts to use
477 */
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500478std::vector<RegistryCallout>
479 getCalloutsWithoutAD(const nlohmann::json& json,
480 const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500481{
482 std::vector<RegistryCallout> calloutEntries;
483
484 // Find the CalloutList to use based on the system type
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500485 const auto& calloutList = findCalloutList(json, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500486
487 // We finally found the callouts, make the objects.
488 for (const auto& callout : calloutList)
489 {
490 calloutEntries.push_back(std::move(makeRegistryCallout(callout)));
491 }
492
493 return calloutEntries;
494}
495
496/**
497 * @brief Returns the callouts to use when an AdditionalData key is
498 * required to find the correct entries.
499 *
500 * The JSON looks like:
501 * {
502 * "ADName": "PROC_NUM",
503 * "CalloutsWithTheirADValues":
504 * [
505 * {
506 * "ADValue": "0",
507 * "Callouts":
508 * [
509 * {
510 * "CalloutList":
511 * [
512 * {
513 * "Priority": "high",
514 * "LocCode": "P1-C5"
515 * }
516 * ]
517 * }
518 * ]
519 * }
520 * ]
521 * }
522 *
523 * Note that the "Callouts" entry above is the same as the top level
524 * entry used when there is no AdditionalData key.
525 *
526 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500527 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500528 * @param[in] additionalData - The AdditionalData property
529 *
530 * @return std::vector<RegistryCallout> - The callouts to use
531 */
532std::vector<RegistryCallout>
533 getCalloutsUsingAD(const nlohmann::json& json,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500534 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500535 const AdditionalData& additionalData)
536{
537 // This indicates which AD field we'll be using
538 auto keyName = json["ADName"].get<std::string>();
539
540 // Get the actual value from the AD data
541 auto adValue = additionalData.getValue(keyName);
542
543 if (!adValue)
544 {
545 // The AdditionalData did not contain the necessary key
546 log<level::WARNING>(
547 "The PEL message registry callouts JSON "
548 "said to use an AdditionalData key that isn't in the "
549 "AdditionalData event log property",
550 entry("ADNAME=%s\n", keyName.c_str()));
551 throw std::runtime_error{
552 "Missing AdditionalData entry for this callout"};
553 }
554
555 const auto& callouts = json["CalloutsWithTheirADValues"];
556
557 // find the entry with that AD value
558 auto it = std::find_if(
559 callouts.begin(), callouts.end(), [adValue](const nlohmann::json& j) {
560 return *adValue == j["ADValue"].get<std::string>();
561 });
562
563 if (it == callouts.end())
564 {
Matt Spinlerf397afc2021-01-29 11:21:44 -0600565 // This can happen if not all possible values were in the
Matt Spinler3d923312022-08-01 09:52:55 -0500566 // message registry and that's fine. There may be a
567 // "CalloutsWhenNoADMatch" section that contains callouts
568 // to use in this case.
569 if (json.contains("CalloutsWhenNoADMatch"))
570 {
571 return getCalloutsWithoutAD(json["CalloutsWhenNoADMatch"],
572 systemNames);
573 }
Matt Spinlerf397afc2021-01-29 11:21:44 -0600574 return std::vector<RegistryCallout>{};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500575 }
576
577 // Proceed to find the callouts possibly based on system type.
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500578 return getCalloutsWithoutAD((*it)["Callouts"], systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500579}
580
Matt Spinler711f1122022-12-15 11:41:20 -0600581/**
582 * @brief Returns the journal capture information
583 *
584 * The JSON looks like:
585 * "JournalCapture": {
586 * "NumLines": 30
587 * }
588 *
589 * "JournalCapture":
590 * {
591 * "Sections": [
592 * {
593 * "SyslogID": "phosphor-log-manager",
594 * "NumLines": 20
595 * }
596 * ]
597 * }
598 *
599 * @param json - The journal capture JSON
600 * @return JournalCapture - The filled in variant
601 */
602JournalCapture getJournalCapture(const nlohmann::json& json)
603{
604 JournalCapture capt;
605
606 // Primary key is either NumLines or Sections.
607 if (json.contains("NumLines"))
608 {
609 capt = json.at("NumLines").get<size_t>();
610 }
611 else if (json.contains("Sections"))
612 {
613 AppCaptureList captures;
614 for (const auto& capture : json.at("Sections"))
615 {
616 AppCapture ac;
617 ac.syslogID = capture.at("SyslogID").get<std::string>();
618 ac.numLines = capture.at("NumLines").get<size_t>();
619 captures.push_back(std::move(ac));
620 }
621
622 capt = captures;
623 }
624 else
625 {
626 log<level::ERR>("JournalCapture section not the right format");
627 throw std::runtime_error{"JournalCapture section not the right format"};
628 }
629
630 return capt;
631}
632
Matt Spinler367144c2019-09-19 15:33:52 -0500633} // namespace helper
634
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800635std::optional<Entry> Registry::lookup(const std::string& name, LookupType type,
636 bool toCache)
Matt Spinler367144c2019-09-19 15:33:52 -0500637{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800638 std::optional<nlohmann::json> registryTmp;
639 auto& registryOpt = (_registry) ? _registry : registryTmp;
640 if (!registryOpt)
Matt Spinler367144c2019-09-19 15:33:52 -0500641 {
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800642 registryOpt = readRegistry(_registryFile);
643 if (!registryOpt)
644 {
645 return std::nullopt;
646 }
647 else if (toCache)
648 {
649 // Save message registry in memory for peltool
650 _registry = std::move(registryTmp);
651 }
Matt Spinler367144c2019-09-19 15:33:52 -0500652 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800653 auto& reg = (_registry) ? _registry : registryTmp;
654 const auto& registry = reg.value();
Matt Spinler367144c2019-09-19 15:33:52 -0500655 // Find an entry with this name in the PEL array.
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800656 auto e = std::find_if(
657 registry["PELs"].begin(), registry["PELs"].end(),
658 [&name, &type](const auto& j) {
659 return ((name == j["Name"] && type == LookupType::name) ||
660 (name == j["SRC"]["ReasonCode"] &&
661 type == LookupType::reasonCode));
662 });
Matt Spinler367144c2019-09-19 15:33:52 -0500663
664 if (e != registry["PELs"].end())
665 {
666 // Fill in the Entry structure from the JSON. Most, but not all, fields
667 // are optional.
668
669 try
670 {
671 Entry entry;
672 entry.name = (*e)["Name"];
Matt Spinler23970b02022-02-25 16:34:46 -0600673
674 if (e->contains("Subsystem"))
675 {
676 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]);
677 }
Matt Spinlere07f9152019-11-01 10:48:36 -0500678
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500679 if (e->contains("ActionFlags"))
Matt Spinlere07f9152019-11-01 10:48:36 -0500680 {
681 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]);
682 }
Matt Spinler367144c2019-09-19 15:33:52 -0500683
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500684 if (e->contains("MfgActionFlags"))
Matt Spinler367144c2019-09-19 15:33:52 -0500685 {
686 entry.mfgActionFlags =
687 helper::getActionFlags((*e)["MfgActionFlags"]);
688 }
689
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500690 if (e->contains("Severity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500691 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500692 entry.severity = helper::getSeverities((*e)["Severity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500693 }
694
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500695 if (e->contains("MfgSeverity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500696 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500697 entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500698 }
699
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500700 if (e->contains("EventType"))
Matt Spinler367144c2019-09-19 15:33:52 -0500701 {
702 entry.eventType = helper::getEventType((*e)["EventType"]);
703 }
704
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500705 if (e->contains("EventScope"))
Matt Spinler367144c2019-09-19 15:33:52 -0500706 {
707 entry.eventScope = helper::getEventScope((*e)["EventScope"]);
708 }
709
Matt Spinler93e29322019-09-20 11:16:15 -0500710 auto& src = (*e)["SRC"];
711 entry.src.reasonCode = helper::getSRCReasonCode(src, name);
712
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500713 if (src.contains("Type"))
Matt Spinler93e29322019-09-20 11:16:15 -0500714 {
715 entry.src.type = helper::getSRCType(src, name);
716 }
717 else
718 {
719 entry.src.type = static_cast<uint8_t>(SRCType::bmcError);
720 }
721
722 // Now that we know the SRC type and reason code,
723 // we can get the component ID.
724 entry.componentID = helper::getComponentID(
725 entry.src.type, entry.src.reasonCode, *e, name);
726
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500727 if (src.contains("Words6To9"))
Matt Spinler93e29322019-09-20 11:16:15 -0500728 {
Patrick Williams2544b412022-10-04 08:41:06 -0500729 entry.src.hexwordADFields = helper::getSRCHexwordFields(src,
730 name);
Matt Spinler93e29322019-09-20 11:16:15 -0500731 }
732
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500733 if (src.contains("SymptomIDFields"))
Matt Spinler93e29322019-09-20 11:16:15 -0500734 {
735 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
736 }
737
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800738 auto& doc = (*e)["Documentation"];
739 entry.doc.message = doc["Message"];
740 entry.doc.description = doc["Description"];
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500741 if (doc.contains("MessageArgSources"))
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800742 {
Matt Spinler3bb15b92022-04-27 09:32:10 -0500743 entry.doc.messageArgSources = doc["MessageArgSources"];
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800744 }
745
Matt Spinlerd8e29002020-04-09 09:11:22 -0500746 // If there are callouts defined, save the JSON for later
747 if (_loadCallouts)
748 {
749 if (e->contains("Callouts"))
750 {
751 entry.callouts = (*e)["Callouts"];
752 }
753 else if (e->contains("CalloutsUsingAD"))
754 {
755 entry.callouts = (*e)["CalloutsUsingAD"];
756 }
757 }
758
Matt Spinler711f1122022-12-15 11:41:20 -0600759 if (e->contains("JournalCapture"))
760 {
761 entry.journalCapture =
762 helper::getJournalCapture((*e)["JournalCapture"]);
763 }
764
Matt Spinler367144c2019-09-19 15:33:52 -0500765 return entry;
766 }
Matt Spinler45796e82022-07-01 11:25:27 -0500767 catch (const std::exception& ex)
Matt Spinler367144c2019-09-19 15:33:52 -0500768 {
769 log<level::ERR>("Found invalid message registry field",
Matt Spinler45796e82022-07-01 11:25:27 -0500770 entry("ERROR=%s", ex.what()));
Matt Spinler367144c2019-09-19 15:33:52 -0500771 }
772 }
773
774 return std::nullopt;
775}
776
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800777std::optional<nlohmann::json>
778 Registry::readRegistry(const std::filesystem::path& registryFile)
779{
780 // Look in /etc first in case someone put a test file there
781 fs::path debugFile{fs::path{debugFilePath} / registryFileName};
782 nlohmann::json registry;
783 std::ifstream file;
784
785 if (fs::exists(debugFile))
786 {
787 log<level::INFO>("Using debug PEL message registry");
788 file.open(debugFile);
789 }
790 else
791 {
792 file.open(registryFile);
793 }
794
795 try
796 {
797 registry = nlohmann::json::parse(file);
798 }
Patrick Williams66491c62021-10-06 12:23:37 -0500799 catch (const std::exception& e)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800800 {
801 log<level::ERR>("Error parsing message registry JSON",
802 entry("JSON_ERROR=%s", e.what()));
803 return std::nullopt;
804 }
805 return registry;
806}
807
Matt Spinler6b427cc2020-04-09 09:42:59 -0500808std::vector<RegistryCallout>
809 Registry::getCallouts(const nlohmann::json& calloutJSON,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500810 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500811 const AdditionalData& additionalData)
812{
813 // The JSON may either use an AdditionalData key
814 // as an index, or not.
815 if (helper::calloutUsesAdditionalData(calloutJSON))
816 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500817 return helper::getCalloutsUsingAD(calloutJSON, systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500818 additionalData);
819 }
820
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500821 return helper::getCalloutsWithoutAD(calloutJSON, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500822}
823
Matt Spinler367144c2019-09-19 15:33:52 -0500824} // namespace message
825} // namespace pels
826} // namespace openpower