blob: c9c416915376ed8eec40682313b16c7d117c2495 [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
Matt Spinler3fe93e92023-04-14 14:06:59 -0500193bool getSRCDeconfigFlag(const nlohmann::json& src)
194{
195 return src["DeconfigFlag"].get<bool>();
196}
197
Matt Spinler93e29322019-09-20 11:16:15 -0500198std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
199 getSRCHexwordFields(const nlohmann::json& src, const std::string& name)
200{
201 std::map<SRC::WordNum, SRC::AdditionalDataField> hexwordFields;
202
203 // Build the map of which AdditionalData fields to use for which SRC words
204
205 // Like:
206 // {
207 // "8":
208 // {
209 // "AdditionalDataPropSource": "TEST"
210 // }
211 //
212 // }
213
214 for (const auto& word : src["Words6To9"].items())
215 {
216 std::string num = word.key();
217 size_t wordNum = std::strtoul(num.c_str(), nullptr, 10);
218
219 if (wordNum == 0)
220 {
221 log<phosphor::logging::level::ERR>(
222 "Invalid SRC word number in message registry",
223 entry("ERROR_NAME=%s", name.c_str()),
224 entry("SRC_WORD_NUM=%s", num.c_str()));
225
226 throw std::runtime_error("Invalid SRC word in message registry");
227 }
228
229 auto attributes = word.value();
Zane Shelleye8db29b2021-11-13 10:34:07 -0600230
231 // Use an empty string for the description if it does not exist.
232 auto itr = attributes.find("Description");
233 std::string desc = (attributes.end() != itr) ? *itr : "";
234
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800235 std::tuple<std::string, std::string> adPropSourceDesc(
Zane Shelleye8db29b2021-11-13 10:34:07 -0600236 attributes["AdditionalDataPropSource"], desc);
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800237 hexwordFields[wordNum] = std::move(adPropSourceDesc);
Matt Spinler93e29322019-09-20 11:16:15 -0500238 }
239
240 if (!hexwordFields.empty())
241 {
242 return hexwordFields;
243 }
244
245 return std::nullopt;
246}
247std::optional<std::vector<SRC::WordNum>>
248 getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name)
249{
250 std::vector<SRC::WordNum> symptomIDFields;
251
252 // Looks like:
253 // "SymptomIDFields": ["SRCWord3", "SRCWord6"],
254
Matt Spinler9d59d582021-05-19 07:57:10 -0600255 for (const std::string field : src["SymptomIDFields"])
Matt Spinler93e29322019-09-20 11:16:15 -0500256 {
257 // Just need the last digit off the end, e.g. SRCWord6.
258 // The schema enforces the format of these.
259 auto srcWordNum = field.substr(field.size() - 1);
260 size_t num = std::strtoul(srcWordNum.c_str(), nullptr, 10);
261 if (num == 0)
262 {
263 log<phosphor::logging::level::ERR>(
264 "Invalid symptom ID field in message registry",
265 entry("ERROR_NAME=%s", name.c_str()),
266 entry("FIELD_NAME=%s", srcWordNum.c_str()));
267
268 throw std::runtime_error("Invalid symptom ID in message registry");
269 }
270 symptomIDFields.push_back(num);
271 }
272 if (!symptomIDFields.empty())
273 {
274 return symptomIDFields;
275 }
276
277 return std::nullopt;
278}
279
280uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode,
281 const nlohmann::json& pelEntry, const std::string& name)
282{
283 uint16_t id = 0;
284
285 // If the ComponentID field is there, use that. Otherwise, if it's a
286 // 0xBD BMC error SRC, use the reasoncode.
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500287 if (pelEntry.contains("ComponentID"))
Matt Spinler93e29322019-09-20 11:16:15 -0500288 {
289 std::string componentID = pelEntry["ComponentID"];
290 id = strtoul(componentID.c_str(), nullptr, 16);
291 }
292 else
293 {
294 // On BMC error SRCs (BD), can just get the component ID from
295 // the first byte of the reason code.
296 if (srcType == static_cast<uint8_t>(SRCType::bmcError))
297 {
298 id = reasonCode & 0xFF00;
299 }
300 else
301 {
302 log<level::ERR>("Missing component ID field in message registry",
303 entry("ERROR_NAME=%s", name.c_str()));
304
305 throw std::runtime_error(
306 "Missing component ID field in message registry");
307 }
308 }
309
310 return id;
311}
312
Matt Spinler6b427cc2020-04-09 09:42:59 -0500313/**
314 * @brief Says if the JSON is the format that contains AdditionalData keys
315 * as in index into them.
316 *
317 * @param[in] json - The highest level callout JSON
318 *
319 * @return bool - If it is the AdditionalData format or not
320 */
321bool calloutUsesAdditionalData(const nlohmann::json& json)
322{
323 return (json.contains("ADName") &&
324 json.contains("CalloutsWithTheirADValues"));
325}
326
327/**
328 * @brief Finds the callouts to use when there is no AdditionalData,
329 * but the system type may be used as a key.
330 *
331 * One entry in the array looks like the following. The System key
332 * is optional and if not present it means that entry applies to
333 * every configuration that doesn't have another entry with a matching
334 * System key.
335 *
336 * {
337 * "System": "system1",
338 * "CalloutList":
339 * [
340 * {
341 * "Priority": "high",
342 * "LocCode": "P1-C1"
343 * },
344 * {
345 * "Priority": "low",
346 * "LocCode": "P1"
347 * }
348 * ]
349 * }
350 */
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500351const nlohmann::json&
352 findCalloutList(const nlohmann::json& json,
353 const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500354{
355 const nlohmann::json* callouts = nullptr;
356
357 if (!json.is_array())
358 {
359 throw std::runtime_error{"findCalloutList was not passed a JSON array"};
360 }
361
362 // The entry with the system type match will take precedence over the entry
363 // without any "System" field in it at all, which will match all other
364 // cases.
365 for (const auto& calloutList : json)
366 {
367 if (calloutList.contains("System"))
368 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500369 if (std::find(systemNames.begin(), systemNames.end(),
370 calloutList["System"].get<std::string>()) !=
371 systemNames.end())
Matt Spinler6b427cc2020-04-09 09:42:59 -0500372 {
373 callouts = &calloutList["CalloutList"];
374 break;
375 }
376 }
377 else
378 {
379 // Any entry with no System key
380 callouts = &calloutList["CalloutList"];
381 }
382 }
383
384 if (!callouts)
385 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500386 std::string types;
387 std::for_each(systemNames.begin(), systemNames.end(),
388 [&types](const auto& t) { types += t + '|'; });
Matt Spinler6b427cc2020-04-09 09:42:59 -0500389 log<level::WARNING>(
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500390 "No matching system name entry or default system name entry "
Matt Spinler6b427cc2020-04-09 09:42:59 -0500391 " for PEL callout list",
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500392 entry("SYSTEMNAMES=%s", types.c_str()));
Matt Spinler6b427cc2020-04-09 09:42:59 -0500393
394 throw std::runtime_error{
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500395 "Could not find a CalloutList JSON for this error and system name"};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500396 }
397
398 return *callouts;
399}
400
401/**
402 * @brief Creates a RegistryCallout based on the input JSON.
403 *
404 * The JSON looks like:
405 * {
406 * "Priority": "high",
407 * "LocCode": "E1"
408 * ...
409 * }
410 *
411 * Schema validation enforces what keys are present.
412 *
413 * @param[in] json - The JSON dictionary entry for a callout
414 *
415 * @return RegistryCallout - A filled in RegistryCallout
416 */
417RegistryCallout makeRegistryCallout(const nlohmann::json& json)
418{
419 RegistryCallout callout;
420
421 callout.priority = "high";
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500422 callout.useInventoryLocCode = false;
Matt Spinler6b427cc2020-04-09 09:42:59 -0500423
424 if (json.contains("Priority"))
425 {
426 callout.priority = json["Priority"].get<std::string>();
427 }
428
429 if (json.contains("LocCode"))
430 {
431 callout.locCode = json["LocCode"].get<std::string>();
432 }
433
434 if (json.contains("Procedure"))
435 {
436 callout.procedure = json["Procedure"].get<std::string>();
437 }
438 else if (json.contains("SymbolicFRU"))
439 {
440 callout.symbolicFRU = json["SymbolicFRU"].get<std::string>();
441 }
442 else if (json.contains("SymbolicFRUTrusted"))
443 {
444 callout.symbolicFRUTrusted =
445 json["SymbolicFRUTrusted"].get<std::string>();
446 }
447
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500448 if (json.contains("UseInventoryLocCode"))
449 {
450 callout.useInventoryLocCode = json["UseInventoryLocCode"].get<bool>();
451 }
452
Matt Spinler6b427cc2020-04-09 09:42:59 -0500453 return callout;
454}
455
456/**
457 * @brief Returns the callouts to use when an AdditionalData key is
458 * required to find the correct entries.
459 *
460 * The System property is used to find which CalloutList to use.
461 * If System is missing, then that CalloutList is valid for
462 * everything.
463 *
464 * The JSON looks like:
465 * [
466 * {
467 * "System": "systemA",
468 * "CalloutList":
469 * [
470 * {
471 * "Priority": "high",
472 * "LocCode": "P1-C5"
473 * }
474 * ]
475 * }
476 * ]
477 *
478 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500479 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500480 *
481 * @return std::vector<RegistryCallout> - The callouts to use
482 */
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500483std::vector<RegistryCallout>
484 getCalloutsWithoutAD(const nlohmann::json& json,
485 const std::vector<std::string>& systemNames)
Matt Spinler6b427cc2020-04-09 09:42:59 -0500486{
487 std::vector<RegistryCallout> calloutEntries;
488
489 // Find the CalloutList to use based on the system type
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500490 const auto& calloutList = findCalloutList(json, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500491
492 // We finally found the callouts, make the objects.
493 for (const auto& callout : calloutList)
494 {
495 calloutEntries.push_back(std::move(makeRegistryCallout(callout)));
496 }
497
498 return calloutEntries;
499}
500
501/**
502 * @brief Returns the callouts to use when an AdditionalData key is
503 * required to find the correct entries.
504 *
505 * The JSON looks like:
506 * {
507 * "ADName": "PROC_NUM",
508 * "CalloutsWithTheirADValues":
509 * [
510 * {
511 * "ADValue": "0",
512 * "Callouts":
513 * [
514 * {
515 * "CalloutList":
516 * [
517 * {
518 * "Priority": "high",
519 * "LocCode": "P1-C5"
520 * }
521 * ]
522 * }
523 * ]
524 * }
525 * ]
526 * }
527 *
528 * Note that the "Callouts" entry above is the same as the top level
529 * entry used when there is no AdditionalData key.
530 *
531 * @param[in] json - The callout JSON
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500532 * @param[in] systemNames - List of compatible system type names
Matt Spinler6b427cc2020-04-09 09:42:59 -0500533 * @param[in] additionalData - The AdditionalData property
534 *
535 * @return std::vector<RegistryCallout> - The callouts to use
536 */
537std::vector<RegistryCallout>
538 getCalloutsUsingAD(const nlohmann::json& json,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500539 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500540 const AdditionalData& additionalData)
541{
542 // This indicates which AD field we'll be using
543 auto keyName = json["ADName"].get<std::string>();
544
545 // Get the actual value from the AD data
546 auto adValue = additionalData.getValue(keyName);
547
548 if (!adValue)
549 {
550 // The AdditionalData did not contain the necessary key
551 log<level::WARNING>(
552 "The PEL message registry callouts JSON "
553 "said to use an AdditionalData key that isn't in the "
554 "AdditionalData event log property",
555 entry("ADNAME=%s\n", keyName.c_str()));
556 throw std::runtime_error{
557 "Missing AdditionalData entry for this callout"};
558 }
559
560 const auto& callouts = json["CalloutsWithTheirADValues"];
561
562 // find the entry with that AD value
563 auto it = std::find_if(
564 callouts.begin(), callouts.end(), [adValue](const nlohmann::json& j) {
565 return *adValue == j["ADValue"].get<std::string>();
566 });
567
568 if (it == callouts.end())
569 {
Matt Spinlerf397afc2021-01-29 11:21:44 -0600570 // This can happen if not all possible values were in the
Matt Spinler3d923312022-08-01 09:52:55 -0500571 // message registry and that's fine. There may be a
572 // "CalloutsWhenNoADMatch" section that contains callouts
573 // to use in this case.
574 if (json.contains("CalloutsWhenNoADMatch"))
575 {
576 return getCalloutsWithoutAD(json["CalloutsWhenNoADMatch"],
577 systemNames);
578 }
Matt Spinlerf397afc2021-01-29 11:21:44 -0600579 return std::vector<RegistryCallout>{};
Matt Spinler6b427cc2020-04-09 09:42:59 -0500580 }
581
582 // Proceed to find the callouts possibly based on system type.
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500583 return getCalloutsWithoutAD((*it)["Callouts"], systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500584}
585
Matt Spinler711f1122022-12-15 11:41:20 -0600586/**
587 * @brief Returns the journal capture information
588 *
589 * The JSON looks like:
590 * "JournalCapture": {
591 * "NumLines": 30
592 * }
593 *
594 * "JournalCapture":
595 * {
596 * "Sections": [
597 * {
598 * "SyslogID": "phosphor-log-manager",
599 * "NumLines": 20
600 * }
601 * ]
602 * }
603 *
604 * @param json - The journal capture JSON
605 * @return JournalCapture - The filled in variant
606 */
607JournalCapture getJournalCapture(const nlohmann::json& json)
608{
609 JournalCapture capt;
610
611 // Primary key is either NumLines or Sections.
612 if (json.contains("NumLines"))
613 {
614 capt = json.at("NumLines").get<size_t>();
615 }
616 else if (json.contains("Sections"))
617 {
618 AppCaptureList captures;
619 for (const auto& capture : json.at("Sections"))
620 {
621 AppCapture ac;
622 ac.syslogID = capture.at("SyslogID").get<std::string>();
623 ac.numLines = capture.at("NumLines").get<size_t>();
624 captures.push_back(std::move(ac));
625 }
626
627 capt = captures;
628 }
629 else
630 {
631 log<level::ERR>("JournalCapture section not the right format");
632 throw std::runtime_error{"JournalCapture section not the right format"};
633 }
634
635 return capt;
636}
637
Matt Spinler367144c2019-09-19 15:33:52 -0500638} // namespace helper
639
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800640std::optional<Entry> Registry::lookup(const std::string& name, LookupType type,
641 bool toCache)
Matt Spinler367144c2019-09-19 15:33:52 -0500642{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800643 std::optional<nlohmann::json> registryTmp;
644 auto& registryOpt = (_registry) ? _registry : registryTmp;
645 if (!registryOpt)
Matt Spinler367144c2019-09-19 15:33:52 -0500646 {
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800647 registryOpt = readRegistry(_registryFile);
648 if (!registryOpt)
649 {
650 return std::nullopt;
651 }
652 else if (toCache)
653 {
654 // Save message registry in memory for peltool
655 _registry = std::move(registryTmp);
656 }
Matt Spinler367144c2019-09-19 15:33:52 -0500657 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800658 auto& reg = (_registry) ? _registry : registryTmp;
659 const auto& registry = reg.value();
Matt Spinler367144c2019-09-19 15:33:52 -0500660 // Find an entry with this name in the PEL array.
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800661 auto e = std::find_if(
662 registry["PELs"].begin(), registry["PELs"].end(),
663 [&name, &type](const auto& j) {
664 return ((name == j["Name"] && type == LookupType::name) ||
665 (name == j["SRC"]["ReasonCode"] &&
666 type == LookupType::reasonCode));
667 });
Matt Spinler367144c2019-09-19 15:33:52 -0500668
669 if (e != registry["PELs"].end())
670 {
671 // Fill in the Entry structure from the JSON. Most, but not all, fields
672 // are optional.
673
674 try
675 {
676 Entry entry;
677 entry.name = (*e)["Name"];
Matt Spinler23970b02022-02-25 16:34:46 -0600678
679 if (e->contains("Subsystem"))
680 {
681 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]);
682 }
Matt Spinlere07f9152019-11-01 10:48:36 -0500683
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500684 if (e->contains("ActionFlags"))
Matt Spinlere07f9152019-11-01 10:48:36 -0500685 {
686 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]);
687 }
Matt Spinler367144c2019-09-19 15:33:52 -0500688
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500689 if (e->contains("MfgActionFlags"))
Matt Spinler367144c2019-09-19 15:33:52 -0500690 {
691 entry.mfgActionFlags =
692 helper::getActionFlags((*e)["MfgActionFlags"]);
693 }
694
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500695 if (e->contains("Severity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500696 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500697 entry.severity = helper::getSeverities((*e)["Severity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500698 }
699
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500700 if (e->contains("MfgSeverity"))
Matt Spinler367144c2019-09-19 15:33:52 -0500701 {
Matt Spinleraadccc82020-04-10 14:33:42 -0500702 entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]);
Matt Spinler367144c2019-09-19 15:33:52 -0500703 }
704
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500705 if (e->contains("EventType"))
Matt Spinler367144c2019-09-19 15:33:52 -0500706 {
707 entry.eventType = helper::getEventType((*e)["EventType"]);
708 }
709
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500710 if (e->contains("EventScope"))
Matt Spinler367144c2019-09-19 15:33:52 -0500711 {
712 entry.eventScope = helper::getEventScope((*e)["EventScope"]);
713 }
714
Matt Spinler93e29322019-09-20 11:16:15 -0500715 auto& src = (*e)["SRC"];
716 entry.src.reasonCode = helper::getSRCReasonCode(src, name);
717
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500718 if (src.contains("Type"))
Matt Spinler93e29322019-09-20 11:16:15 -0500719 {
720 entry.src.type = helper::getSRCType(src, name);
721 }
722 else
723 {
724 entry.src.type = static_cast<uint8_t>(SRCType::bmcError);
725 }
726
727 // Now that we know the SRC type and reason code,
728 // we can get the component ID.
729 entry.componentID = helper::getComponentID(
730 entry.src.type, entry.src.reasonCode, *e, name);
731
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500732 if (src.contains("Words6To9"))
Matt Spinler93e29322019-09-20 11:16:15 -0500733 {
Patrick Williams2544b412022-10-04 08:41:06 -0500734 entry.src.hexwordADFields = helper::getSRCHexwordFields(src,
735 name);
Matt Spinler93e29322019-09-20 11:16:15 -0500736 }
737
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500738 if (src.contains("SymptomIDFields"))
Matt Spinler93e29322019-09-20 11:16:15 -0500739 {
740 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
741 }
742
Matt Spinler3fe93e92023-04-14 14:06:59 -0500743 if (src.contains("DeconfigFlag"))
744 {
745 entry.src.deconfigFlag = helper::getSRCDeconfigFlag(src);
746 }
747
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800748 auto& doc = (*e)["Documentation"];
749 entry.doc.message = doc["Message"];
750 entry.doc.description = doc["Description"];
Matt Spinler3ace0cf2020-04-09 15:16:57 -0500751 if (doc.contains("MessageArgSources"))
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800752 {
Matt Spinler3bb15b92022-04-27 09:32:10 -0500753 entry.doc.messageArgSources = doc["MessageArgSources"];
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800754 }
755
Matt Spinlerd8e29002020-04-09 09:11:22 -0500756 // If there are callouts defined, save the JSON for later
757 if (_loadCallouts)
758 {
759 if (e->contains("Callouts"))
760 {
761 entry.callouts = (*e)["Callouts"];
762 }
763 else if (e->contains("CalloutsUsingAD"))
764 {
765 entry.callouts = (*e)["CalloutsUsingAD"];
766 }
767 }
768
Matt Spinler711f1122022-12-15 11:41:20 -0600769 if (e->contains("JournalCapture"))
770 {
771 entry.journalCapture =
772 helper::getJournalCapture((*e)["JournalCapture"]);
773 }
774
Matt Spinler367144c2019-09-19 15:33:52 -0500775 return entry;
776 }
Matt Spinler45796e82022-07-01 11:25:27 -0500777 catch (const std::exception& ex)
Matt Spinler367144c2019-09-19 15:33:52 -0500778 {
779 log<level::ERR>("Found invalid message registry field",
Matt Spinler45796e82022-07-01 11:25:27 -0500780 entry("ERROR=%s", ex.what()));
Matt Spinler367144c2019-09-19 15:33:52 -0500781 }
782 }
783
784 return std::nullopt;
785}
786
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800787std::optional<nlohmann::json>
788 Registry::readRegistry(const std::filesystem::path& registryFile)
789{
790 // Look in /etc first in case someone put a test file there
791 fs::path debugFile{fs::path{debugFilePath} / registryFileName};
792 nlohmann::json registry;
793 std::ifstream file;
794
795 if (fs::exists(debugFile))
796 {
797 log<level::INFO>("Using debug PEL message registry");
798 file.open(debugFile);
799 }
800 else
801 {
802 file.open(registryFile);
803 }
804
805 try
806 {
807 registry = nlohmann::json::parse(file);
808 }
Patrick Williams66491c62021-10-06 12:23:37 -0500809 catch (const std::exception& e)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800810 {
811 log<level::ERR>("Error parsing message registry JSON",
812 entry("JSON_ERROR=%s", e.what()));
813 return std::nullopt;
814 }
815 return registry;
816}
817
Matt Spinler6b427cc2020-04-09 09:42:59 -0500818std::vector<RegistryCallout>
819 Registry::getCallouts(const nlohmann::json& calloutJSON,
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500820 const std::vector<std::string>& systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500821 const AdditionalData& additionalData)
822{
823 // The JSON may either use an AdditionalData key
824 // as an index, or not.
825 if (helper::calloutUsesAdditionalData(calloutJSON))
826 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500827 return helper::getCalloutsUsingAD(calloutJSON, systemNames,
Matt Spinler6b427cc2020-04-09 09:42:59 -0500828 additionalData);
829 }
830
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500831 return helper::getCalloutsWithoutAD(calloutJSON, systemNames);
Matt Spinler6b427cc2020-04-09 09:42:59 -0500832}
833
Matt Spinler367144c2019-09-19 15:33:52 -0500834} // namespace message
835} // namespace pels
836} // namespace openpower