blob: 03140815e3e25e9fc9f2896a0fc5f17e1b653de4 [file] [log] [blame]
#pragma once
#include "additional_data.hpp"
#include <filesystem>
#include <nlohmann/json.hpp>
#include <optional>
#include <string>
#include <vector>
namespace openpower
{
namespace pels
{
namespace message
{
constexpr auto registryFileName = "message_registry.json";
enum class LookupType
{
name = 0,
reasonCode = 1
};
/**
* @brief A possible severity/system type combination
*
* If there is no system type defined for this entry,
* then the system field will be empty.
*/
struct RegistrySeverity
{
std::string system;
uint8_t severity;
};
/**
* @brief Represents the Documentation related fields in the message registry.
* It is part of the 'Entry' structure that will be filled in when
* an error is looked up in the registry.
*
* If a field is wrapped by std::optional, it means the field is
* optional in the JSON and higher level code knows how to handle it.
*/
struct DOC
{
/**
* @brief Description of error
*/
std::string description;
/**
* @brief Error message field
*/
std::string message;
/**
* @brief An optional vector of SRC word 6-9 to use as the source of the
* numeric arguments that will be substituted into any placeholder
* in the Message field.
*/
std::optional<std::vector<std::string>> messageArgSources;
/**
* @brief An optional vector of string that forms the Notes field.
*/
std::optional<std::vector<std::string>> notes;
};
/**
* @brief Represents the SRC related fields in the message registry.
* It is part of the 'Entry' structure that will be filled in when
* an error is looked up in the registry.
*
* If a field is wrapped by std::optional, it means the field is
* optional in the JSON and higher level code knows how to handle it.
*/
struct SRC
{
/**
* @brief SRC type - The first byte of the ASCII string
*/
uint8_t type;
/**
* @brief The SRC reason code (2nd half of 4B 'ASCII string' word)
*/
uint16_t reasonCode;
/**
* @brief An optional vector of SRC hexword numbers that should be used
* along with the SRC ASCII string to build the Symptom ID, which
* is a field in the Extended Header section.
*/
using WordNum = size_t;
std::optional<std::vector<WordNum>> symptomID;
/**
* @brief Which AdditionalData fields to use to fill in the user defined
* SRC hexwords.
*
* For example, if the AdditionalData event log property contained
* "CHIPNUM=42" and this map contained {6, {"CHIPNUM", "DESC"}}, then the
* code would put 42 into SRC hexword 6.
*
* AdditionalDataField specifies two fields from the SRC entry in the
* message registry: "AdditionalDataPropSource" and "Description"
*/
using AdditionalDataField = std::tuple<std::string, std::string>;
std::optional<std::map<WordNum, AdditionalDataField>> hexwordADFields;
SRC() : type(0), reasonCode(0)
{
}
};
/**
* @brief Represents a message registry entry, which is used for creating a
* PEL from an OpenBMC event log.
*/
struct Entry
{
/**
* @brief The error name, like "xyz.openbmc_project.Error.Foo".
*/
std::string name;
/**
* @brief The component ID of the PEL creator.
*/
uint16_t componentID;
/**
* @brief The PEL subsystem field.
*/
std::optional<uint8_t> subsystem;
/**
* @brief The optional PEL severity field. If not specified, the PEL
* will use the severity of the OpenBMC event log.
*
* If the system type is specified in any of the entries in the vector,
* then the system type will be needed to find the actual severity.
*/
std::optional<std::vector<RegistrySeverity>> severity;
/**
* @brief The optional severity field to use when in manufacturing tolerance
* mode. It behaves like the severity field above.
*/
std::optional<std::vector<RegistrySeverity>> mfgSeverity;
/**
* @brief The PEL action flags field.
*/
std::optional<uint16_t> actionFlags;
/**
* @brief The optional action flags to use instead when in manufacturing
* tolerance mode.
*/
std::optional<uint16_t> mfgActionFlags;
/**
* @brief The PEL event type field. If not specified, higher level code
* will decide the value.
*/
std::optional<uint8_t> eventType;
/**
* @brief The PEL event scope field. If not specified, higher level code
* will decide the value.
*/
std::optional<uint8_t> eventScope;
/**
* The SRC related fields.
*/
SRC src;
/**
* The Documentation related fields.
*/
DOC doc;
/**
* @brief The callout JSON, if the entry has callouts.
*/
std::optional<nlohmann::json> callouts;
};
/**
* @brief Holds callout information pulled out of the JSON.
*/
struct RegistryCallout
{
std::string priority;
std::string locCode;
std::string procedure;
std::string symbolicFRU;
std::string symbolicFRUTrusted;
bool useInventoryLocCode;
};
/**
* @class Registry
*
* This class wraps the message registry JSON data and allows one to find
* the message registry entry pertaining to the error name.
*
* So that new registry files can easily be tested, the code will look for
* /etc/phosphor-logging/message_registry.json before looking for the real
* path.
*/
class Registry
{
public:
Registry() = delete;
~Registry() = default;
Registry(const Registry&) = default;
Registry& operator=(const Registry&) = default;
Registry(Registry&&) = default;
Registry& operator=(Registry&&) = default;
/**
* @brief Constructor
*
* Will load the callout JSON.
*
* @param[in] registryFile - The path to the file.
*/
explicit Registry(const std::filesystem::path& registryFile) :
Registry(registryFile, true)
{
}
/**
* @brief Constructor
*
* This version contains a parameter that allows the callout JSON
* to be saved in the Entry struct or not, as it isn't needed at
* all in some cases.
*
* @param[in] registryFile - The path to the file.
* @param[in] loadCallouts - If the callout JSON should be saved.
*/
explicit Registry(const std::filesystem::path& registryFile,
bool loadCallouts) :
_registryFile(registryFile),
_loadCallouts(loadCallouts)
{
}
/**
* @brief Find a registry entry based on its error name or reason code.
*
* This function does do some basic sanity checking on the JSON contents,
* but there is also an external program that enforces a schema on the
* registry JSON that should catch all of these problems ahead of time.
*
* @param[in] name - The error name, like xyz.openbmc_project.Error.Foo
* - OR
* - The reason code, like 0x1001
* @param[in] type - LookupType enum value
* @param[in] toCache - boolean to cache registry in memory
* @return optional<Entry> A filled in message registry structure if
* found, otherwise an empty optional object.
*/
std::optional<Entry> lookup(const std::string& name, LookupType type,
bool toCache = false);
/**
* @brief Find the callouts to put into the PEL based on the calloutJSON
* data.
*
* The system type and AdditionalData are used to index into the correct
* callout table.
*
* Throws exceptions on failures.
*
* @param[in] calloutJSON - Where to look up the callouts
* @param[in] systemNames - List of compatible system type names
* @param[in] additionalData - The AdditionalData property
*
* @return std::vector<RegistryCallout> - The callouts to use
*/
static std::vector<RegistryCallout>
getCallouts(const nlohmann::json& calloutJSON,
const std::vector<std::string>& systemNames,
const AdditionalData& additionalData);
private:
/**
* @brief Parse message registry file using nlohmann::json
* @param[in] registryFile - The message registry JSON file
* @return optional<nlohmann::json> The full message registry object or an
* empty optional object upon failure.
*/
std::optional<nlohmann::json>
readRegistry(const std::filesystem::path& registryFile);
/**
* @brief The path to the registry JSON file.
*/
std::filesystem::path _registryFile;
/**
* @brief The full message registry object.
*/
std::optional<nlohmann::json> _registry;
/**
* @brief If the callout JSON should be saved in the Entry on lookup.
*/
bool _loadCallouts;
};
namespace helper
{
/**
* @brief A helper function to get the PEL subsystem value based on
* the registry subsystem name.
*
* @param[in] subsystemName - The registry name for the subsystem
*
* @return uint8_t The PEL subsystem value
*/
uint8_t getSubsystem(const std::string& subsystemName);
/**
* @brief A helper function to get the PEL severity value based on
* the registry severity name.
*
* @param[in] severityName - The registry name for the severity
*
* @return uint8_t The PEL severity value
*/
uint8_t getSeverity(const std::string& severityName);
/**
* @brief Returns all of the system type/severity values found
* in the severity JSON passed in.
*
* The JSON is either a simple string, like:
* "unrecoverable"
* or an array of system type/severity pairs, like:
* [
* {
* "System": "1",
* "SevValue": "predictive"
* },
* {
* "System": "2",
* "SevValue": "recovered"
* }
* ]
*
* @param[in] severity - The severity JSON
* @return The list of severity/system combinations. If the System key
* wasn't used, then that field will be empty in the structure.
*/
std::vector<RegistrySeverity> getSeverities(const nlohmann::json& severity);
/**
* @brief A helper function to get the action flags value based on
* the action flag names used in the registry.
*
* @param[in] flags - The list of flag names from the registry.
*
* @return uint16_t - The bitfield of flags used in the PEL.
*/
uint16_t getActionFlags(const std::vector<std::string>& flags);
/**
* @brief A helper function to get the PEL event type value based on
* the registry event type name.
*
* @param[in] eventTypeName - The registry name for the event type
*
* @return uint8_t The PEL event type value
*/
uint8_t getEventType(const std::string& eventTypeName);
/**
* @brief A helper function to get the PEL event scope value based on
* the registry event scope name.
*
* @param[in] eventScopeName - The registry name for the event scope
*
* @return uint8_t The PEL event scope value
*/
uint8_t getEventScope(const std::string& eventScopeName);
/**
* @brief Reads the "ReasonCode" field out of JSON and converts the string value
* such as "0x5555" to a uint16 like 0x5555.
*
* @param[in] src - The message registry SRC dictionary to read from
* @param[in] name - The error name, to use in a trace if things go awry.
*
* @return uint16_t - The reason code
*/
uint16_t getSRCReasonCode(const nlohmann::json& src, const std::string& name);
/**
* @brief Reads the "Type" field out of JSON and converts it to the SRC::Type
* value.
*
* @param[in] src - The message registry SRC dictionary to read from
* @param[in] name - The error name, to use in a trace if things go awry.
*
* @return uint8_t - The SRC type value, like 0x11
*/
uint8_t getSRCType(const nlohmann::json& src, const std::string& name);
/**
* @brief Reads the "Words6To9" field out of JSON and converts it to a map
* of the SRC word number to the AdditionalData property field used
* to fill it in with.
*
* @param[in] src - The message registry SRC dictionary to read from
* @param[in] name - The error name, to use in a trace if things go awry.
*
* @return std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
*/
std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
getSRCHexwordFields(const nlohmann::json& src, const std::string& name);
/**
* @brief Reads the "SymptomIDFields" field out of JSON and converts it to
* a vector of SRC word numbers.
*
* @param[in] src - The message registry SRC dictionary to read from
* @param[in] name - The error name, to use in a trace if things go awry.
*
* @return std::optional<std::vector<SRC::WordNum>>
*/
std::optional<std::vector<SRC::WordNum>>
getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name);
/**
* @brief Reads the "ComponentID" field out of JSON and converts it to a
* uint16_t like 0xFF00.
*
* The ComponentID JSON field is only required if the SRC type isn't a BD
* BMC SRC, because for those SRCs it can be inferred from the upper byte
* of the SRC reasoncode.
*
* @param[in] srcType - The SRC type
* @param[in] reasonCode - The SRC reason code
* @param[in] pelEntry - The PEL entry JSON
* @param[in] name - The error name, to use in a trace if things go awry.
*
* @return uin16_t - The component ID, like 0xFF00
*/
uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode,
const nlohmann::json& pelEntry,
const std::string& name);
} // namespace helper
} // namespace message
} // namespace pels
} // namespace openpower