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