blob: a86ccb39abd7d53bcfb87eb6ec9c4a0f9460e546 [file] [log] [blame]
Matt Spinleracb7c102020-01-10 13:49:22 -06001/**
2 * Copyright © 2020 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 */
16
17#include "user_data_json.hpp"
18
19#include "pel_types.hpp"
20#include "user_data_formats.hpp"
21
22#include <fifo_map.hpp>
23#include <iomanip>
24#include <nlohmann/json.hpp>
25#include <phosphor-logging/log.hpp>
26#include <sstream>
27
28namespace openpower::pels::user_data
29{
30
31using namespace phosphor::logging;
32
33// Use fifo_map as nlohmann::json's map. We are just ignoring the 'less'
34// compare. With this map the keys are kept in FIFO order.
35template <class K, class V, class dummy_compare, class A>
36using fifoMap = nlohmann::fifo_map<K, V, nlohmann::fifo_map_compare<K>, A>;
37using fifoJSON = nlohmann::basic_json<fifoMap>;
38
39/**
40 * @brief Returns a JSON string for use by PEL::printSectionInJSON().
41 *
42 * The returning string will contain a JSON object, but without
43 * the outer {}. If the input JSON isn't a JSON object (dict), then
44 * one will be created with the input added to a 'Data' key.
45 *
46 * @param[in] json - The JSON to convert to a string
47 *
48 * @return std::string - The JSON string
49 */
50std::string prettyJSON(uint16_t componentID, uint8_t subType, uint8_t version,
51 const fifoJSON& json)
52{
53 fifoJSON output;
54 output["Section Version"] = std::to_string(version);
55 output["Sub-section type"] = std::to_string(subType);
56
57 char value[10];
58 sprintf(value, "0x%04X", componentID);
59 output["Created by"] = std::string{value};
60
61 if (!json.is_object())
62 {
63 output["Data"] = json;
64 }
65 else
66 {
67 for (const auto& [key, value] : json.items())
68 {
69 output[key] = value;
70 }
71 }
72
73 // Let nlohmann do the pretty printing.
74 std::stringstream stream;
75 stream << std::setw(4) << output;
76
77 auto jsonString = stream.str();
78
79 // Now it looks like:
80 // {
81 // "Section Version": ...
82 // ...
83 // }
84
85 // Since PEL::printSectionInJSON() will supply the outer { }s,
86 // remove the existing ones.
87
88 // Replace the { and the following newline, and the } and its
89 // preceeding newline.
90 jsonString.erase(0, 2);
91
92 auto pos = jsonString.find_last_of('}');
93 jsonString.erase(pos - 1);
94
95 return jsonString;
96}
97
98/**
99 * @brief Convert to an appropriate JSON string as the data is one of
100 * the formats that we natively support.
101 *
102 * @param[in] componentID - The comp ID from the UserData section header
103 * @param[in] subType - The subtype from the UserData section header
104 * @param[in] version - The version from the UserData section header
105 * @param[in] data - The data itself
106 *
107 * @return std::optional<std::string> - The JSON string if it could be created,
108 * else std::nullopt.
109 */
110std::optional<std::string>
111 getBuiltinFormatJSON(uint16_t componentID, uint8_t subType, uint8_t version,
112 const std::vector<uint8_t>& data)
113{
114 switch (subType)
115 {
116 case static_cast<uint8_t>(UserDataFormat::json):
117 {
118 std::string jsonString{data.begin(), data.begin() + data.size()};
119
120 fifoJSON json = nlohmann::json::parse(jsonString);
121
122 return prettyJSON(componentID, subType, version, json);
123 }
124 default:
125 break;
126 }
127 return std::nullopt;
128}
129
130std::optional<std::string> getJSON(uint16_t componentID, uint8_t subType,
131 uint8_t version,
132 const std::vector<uint8_t>& data)
133{
134 try
135 {
136 switch (componentID)
137 {
138 case static_cast<uint16_t>(ComponentID::phosphorLogging):
139 return getBuiltinFormatJSON(componentID, subType, version,
140 data);
141 default:
142 break;
143 }
144 }
145 catch (std::exception& e)
146 {
147 log<level::ERR>("Failed parsing UserData", entry("ERROR=%s", e.what()));
148 }
149
150 return std::nullopt;
151}
152
153} // namespace openpower::pels::user_data