blob: 34da02f573fa9846166e2f7381c246803950a221 [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
Harisuddin Mohamed Isabebeb942020-03-12 17:12:24 +080019#include "json_utils.hpp"
Matt Spinleracb7c102020-01-10 13:49:22 -060020#include "pel_types.hpp"
Harisuddin Mohamed Isabebeb942020-03-12 17:12:24 +080021#include "pel_values.hpp"
Matt Spinler18207142020-03-26 14:45:54 -050022#include "stream.hpp"
Matt Spinleracb7c102020-01-10 13:49:22 -060023#include "user_data_formats.hpp"
24
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +080025#include <Python.h>
26
Matt Spinleracb7c102020-01-10 13:49:22 -060027#include <nlohmann/json.hpp>
Matt Spinler7cc3aea2023-07-07 16:24:57 -050028#include <phosphor-logging/lg2.hpp>
Patrick Williams2544b412022-10-04 08:41:06 -050029
30#include <iomanip>
Matt Spinleracb7c102020-01-10 13:49:22 -060031#include <sstream>
32
33namespace openpower::pels::user_data
34{
Harisuddin Mohamed Isabebeb942020-03-12 17:12:24 +080035namespace pv = openpower::pels::pel_values;
Sumit Kumar516935a2021-04-14 13:00:54 -050036using orderedJSON = nlohmann::ordered_json;
Matt Spinleracb7c102020-01-10 13:49:22 -060037
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +080038void pyDecRef(PyObject* pyObj)
39{
40 Py_XDECREF(pyObj);
41}
42
Matt Spinleracb7c102020-01-10 13:49:22 -060043/**
44 * @brief Returns a JSON string for use by PEL::printSectionInJSON().
45 *
46 * The returning string will contain a JSON object, but without
47 * the outer {}. If the input JSON isn't a JSON object (dict), then
48 * one will be created with the input added to a 'Data' key.
49 *
Matt Spinlerb832aa52023-03-21 15:32:34 -050050 * @param[in] creatorID - The creator ID for the PEL
51 *
Matt Spinleracb7c102020-01-10 13:49:22 -060052 * @param[in] json - The JSON to convert to a string
53 *
54 * @return std::string - The JSON string
55 */
56std::string prettyJSON(uint16_t componentID, uint8_t subType, uint8_t version,
Matt Spinlerb832aa52023-03-21 15:32:34 -050057 uint8_t creatorID, const orderedJSON& json)
Matt Spinleracb7c102020-01-10 13:49:22 -060058{
Sumit Kumar516935a2021-04-14 13:00:54 -050059 orderedJSON output;
Harisuddin Mohamed Isabebeb942020-03-12 17:12:24 +080060 output[pv::sectionVer] = std::to_string(version);
61 output[pv::subSection] = std::to_string(subType);
Matt Spinlerb832aa52023-03-21 15:32:34 -050062 output[pv::createdBy] = getComponentName(componentID, creatorID);
Matt Spinleracb7c102020-01-10 13:49:22 -060063
64 if (!json.is_object())
65 {
66 output["Data"] = json;
67 }
68 else
69 {
70 for (const auto& [key, value] : json.items())
71 {
72 output[key] = value;
73 }
74 }
75
76 // Let nlohmann do the pretty printing.
77 std::stringstream stream;
78 stream << std::setw(4) << output;
79
80 auto jsonString = stream.str();
81
82 // Now it looks like:
83 // {
84 // "Section Version": ...
85 // ...
86 // }
87
88 // Since PEL::printSectionInJSON() will supply the outer { }s,
89 // remove the existing ones.
90
91 // Replace the { and the following newline, and the } and its
92 // preceeding newline.
93 jsonString.erase(0, 2);
94
95 auto pos = jsonString.find_last_of('}');
96 jsonString.erase(pos - 1);
97
98 return jsonString;
99}
100
101/**
Matt Spinler18207142020-03-26 14:45:54 -0500102 * @brief Return a JSON string from the passed in CBOR data.
103 *
104 * @param[in] componentID - The comp ID from the UserData section header
105 * @param[in] subType - The subtype from the UserData section header
106 * @param[in] version - The version from the UserData section header
Matt Spinlerb832aa52023-03-21 15:32:34 -0500107 * @param[in] creatorID - The creator ID for the PEL
Matt Spinler18207142020-03-26 14:45:54 -0500108 * @param[in] data - The CBOR data
109 *
110 * @return std::string - The JSON string
111 */
112std::string getCBORJSON(uint16_t componentID, uint8_t subType, uint8_t version,
Matt Spinlerb832aa52023-03-21 15:32:34 -0500113 uint8_t creatorID, const std::vector<uint8_t>& data)
Matt Spinler18207142020-03-26 14:45:54 -0500114{
115 // The CBOR parser needs the pad bytes added to 4 byte align
116 // removed. The number of bytes added to the pad is on the
117 // very end, so will remove both fields before parsing.
118
119 // If the data vector is too short, an exception will get
120 // thrown which will be handled up the call stack.
121
122 auto cborData = data;
123 uint32_t pad{};
124
125 Stream stream{cborData};
126 stream.offset(cborData.size() - 4);
127 stream >> pad;
128
129 if (cborData.size() > (pad + sizeof(pad)))
130 {
131 cborData.resize(data.size() - sizeof(pad) - pad);
132 }
133
Matt Spinlerbb1c1d52021-06-03 13:18:48 -0600134 orderedJSON json = orderedJSON::from_cbor(cborData);
Matt Spinler18207142020-03-26 14:45:54 -0500135
Matt Spinlerb832aa52023-03-21 15:32:34 -0500136 return prettyJSON(componentID, subType, version, creatorID, json);
Matt Spinler18207142020-03-26 14:45:54 -0500137}
138
139/**
140 * @brief Return a JSON string from the passed in text data.
141 *
Harisuddin Mohamed Isaaadf28f2020-09-29 22:10:52 +0800142 * The function breaks up the input text into a vector of strings with
143 * newline as separator and converts that into JSON. It will convert any
144 * unprintable characters to periods.
Matt Spinler18207142020-03-26 14:45:54 -0500145 *
146 * @param[in] componentID - The comp ID from the UserData section header
147 * @param[in] subType - The subtype from the UserData section header
148 * @param[in] version - The version from the UserData section header
Matt Spinlerb832aa52023-03-21 15:32:34 -0500149 * @param[in] creatorID - The creator ID for the PEL
Matt Spinler18207142020-03-26 14:45:54 -0500150 * @param[in] data - The CBOR data
151 *
152 * @return std::string - The JSON string
153 */
154std::string getTextJSON(uint16_t componentID, uint8_t subType, uint8_t version,
Matt Spinlerb832aa52023-03-21 15:32:34 -0500155 uint8_t creatorID, const std::vector<uint8_t>& data)
Matt Spinler18207142020-03-26 14:45:54 -0500156{
Matt Spinler18207142020-03-26 14:45:54 -0500157 std::vector<std::string> text;
158 size_t startPos = 0;
Matt Spinler18207142020-03-26 14:45:54 -0500159
Harisuddin Mohamed Isaaadf28f2020-09-29 22:10:52 +0800160 // Converts any unprintable characters to periods
Matt Spinler18207142020-03-26 14:45:54 -0500161 auto validate = [](char& ch) {
162 if ((ch < ' ') || (ch > '~'))
163 {
164 ch = '.';
165 }
166 };
167
Harisuddin Mohamed Isaaadf28f2020-09-29 22:10:52 +0800168 // Break up the data into an array of strings with newline as separator
169 for (size_t pos = 0; pos < data.size(); ++pos)
Matt Spinler18207142020-03-26 14:45:54 -0500170 {
Harisuddin Mohamed Isaaadf28f2020-09-29 22:10:52 +0800171 if (data[pos] == '\n')
Matt Spinler18207142020-03-26 14:45:54 -0500172 {
173 std::string line{reinterpret_cast<const char*>(&data[startPos]),
Harisuddin Mohamed Isaaadf28f2020-09-29 22:10:52 +0800174 pos - startPos};
Matt Spinler18207142020-03-26 14:45:54 -0500175 std::for_each(line.begin(), line.end(), validate);
176 text.push_back(std::move(line));
Harisuddin Mohamed Isaaadf28f2020-09-29 22:10:52 +0800177 startPos = pos + 1;
Matt Spinler18207142020-03-26 14:45:54 -0500178 }
Harisuddin Mohamed Isaaadf28f2020-09-29 22:10:52 +0800179 }
180 if (startPos < data.size())
181 {
182 std::string line{reinterpret_cast<const char*>(&data[startPos]),
183 data.size() - startPos};
184 std::for_each(line.begin(), line.end(), validate);
185 text.push_back(std::move(line));
Matt Spinler18207142020-03-26 14:45:54 -0500186 }
187
Sumit Kumar516935a2021-04-14 13:00:54 -0500188 orderedJSON json = text;
Matt Spinlerb832aa52023-03-21 15:32:34 -0500189 return prettyJSON(componentID, subType, version, creatorID, json);
Matt Spinler18207142020-03-26 14:45:54 -0500190}
191
192/**
Matt Spinleracb7c102020-01-10 13:49:22 -0600193 * @brief Convert to an appropriate JSON string as the data is one of
194 * the formats that we natively support.
195 *
196 * @param[in] componentID - The comp ID from the UserData section header
197 * @param[in] subType - The subtype from the UserData section header
198 * @param[in] version - The version from the UserData section header
199 * @param[in] data - The data itself
200 *
201 * @return std::optional<std::string> - The JSON string if it could be created,
202 * else std::nullopt.
203 */
204std::optional<std::string>
205 getBuiltinFormatJSON(uint16_t componentID, uint8_t subType, uint8_t version,
Matt Spinlerb832aa52023-03-21 15:32:34 -0500206 const std::vector<uint8_t>& data, uint8_t creatorID)
Matt Spinleracb7c102020-01-10 13:49:22 -0600207{
208 switch (subType)
209 {
210 case static_cast<uint8_t>(UserDataFormat::json):
211 {
212 std::string jsonString{data.begin(), data.begin() + data.size()};
213
Matt Spinlerbb1c1d52021-06-03 13:18:48 -0600214 orderedJSON json = orderedJSON::parse(jsonString);
Matt Spinleracb7c102020-01-10 13:49:22 -0600215
Matt Spinlerb832aa52023-03-21 15:32:34 -0500216 return prettyJSON(componentID, subType, version, creatorID, json);
Matt Spinleracb7c102020-01-10 13:49:22 -0600217 }
Matt Spinler18207142020-03-26 14:45:54 -0500218 case static_cast<uint8_t>(UserDataFormat::cbor):
219 {
Matt Spinlerb832aa52023-03-21 15:32:34 -0500220 return getCBORJSON(componentID, subType, version, creatorID, data);
Matt Spinler18207142020-03-26 14:45:54 -0500221 }
222 case static_cast<uint8_t>(UserDataFormat::text):
223 {
Matt Spinlerb832aa52023-03-21 15:32:34 -0500224 return getTextJSON(componentID, subType, version, creatorID, data);
Matt Spinler18207142020-03-26 14:45:54 -0500225 }
Matt Spinleracb7c102020-01-10 13:49:22 -0600226 default:
227 break;
228 }
229 return std::nullopt;
230}
231
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800232/**
233 * @brief Call Python modules to parse the data into a JSON string
234 *
235 * The module to call is based on the Creator Subsystem ID and the Component
236 * ID under the namespace "udparsers". For example: "udparsers.xyyyy.xyyyy"
237 * where "x" is the Creator Subsystem ID and "yyyy" is the Component ID.
238 *
239 * All modules must provide the following:
240 * Function: parseUDToJson
241 * Argument list:
242 * 1. (int) Sub-section type
243 * 2. (int) Section version
244 * 3. (memoryview): Data
245 *-Return data:
246 * 1. (str) JSON string
247 *
248 * @param[in] componentID - The comp ID from the UserData section header
249 * @param[in] subType - The subtype from the UserData section header
250 * @param[in] version - The version from the UserData section header
251 * @param[in] data - The data itself
252 * @param[in] creatorID - The creatorID from the PrivateHeader section
253 * @return std::optional<std::string> - The JSON string if it could be created,
254 * else std::nullopt
255 */
256std::optional<std::string> getPythonJSON(uint16_t componentID, uint8_t subType,
257 uint8_t version,
258 const std::vector<uint8_t>& data,
259 uint8_t creatorID)
260{
Matt Spinlerbe952d22022-07-01 11:30:11 -0500261 PyObject *pName, *pModule, *eType, *eValue, *eTraceback, *pKey;
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800262 std::string pErrStr;
263 std::string module = getNumberString("%c", tolower(creatorID)) +
264 getNumberString("%04x", componentID);
265 pName = PyUnicode_FromString(
266 std::string("udparsers." + module + "." + module).c_str());
267 std::unique_ptr<PyObject, decltype(&pyDecRef)> modNamePtr(pName, &pyDecRef);
268 pModule = PyImport_Import(pName);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800269 if (pModule == NULL)
270 {
271 pErrStr = "No error string found";
272 PyErr_Fetch(&eType, &eValue, &eTraceback);
Harisuddin Mohamed Isad5c31362021-05-29 13:33:39 +0800273 if (eType)
274 {
275 Py_XDECREF(eType);
276 }
277 if (eTraceback)
278 {
279 Py_XDECREF(eTraceback);
280 }
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800281 if (eValue)
282 {
283 PyObject* pStr = PyObject_Str(eValue);
Harisuddin Mohamed Isad5c31362021-05-29 13:33:39 +0800284 Py_XDECREF(eValue);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800285 if (pStr)
286 {
287 pErrStr = PyUnicode_AsUTF8(pStr);
Harisuddin Mohamed Isad5c31362021-05-29 13:33:39 +0800288 Py_XDECREF(pStr);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800289 }
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800290 }
291 }
292 else
293 {
Harisuddin Mohamed Isad5c31362021-05-29 13:33:39 +0800294 std::unique_ptr<PyObject, decltype(&pyDecRef)> modPtr(pModule,
295 &pyDecRef);
296 std::string funcToCall = "parseUDToJson";
297 pKey = PyUnicode_FromString(funcToCall.c_str());
298 std::unique_ptr<PyObject, decltype(&pyDecRef)> keyPtr(pKey, &pyDecRef);
Matt Spinlerbe952d22022-07-01 11:30:11 -0500299 PyObject* pDict = PyModule_GetDict(pModule);
Harisuddin Mohamed Isad5c31362021-05-29 13:33:39 +0800300 Py_INCREF(pDict);
301 if (!PyDict_Contains(pDict, pKey))
302 {
303 Py_DECREF(pDict);
Matt Spinler7cc3aea2023-07-07 16:24:57 -0500304 lg2::error("Python module error. Function missing: {FUNC}, "
305 "module = {MODULE}, subtype = {SUBTYPE}, "
306 "version = {VERSION}, data length = {LEN}",
307 "FUNC", funcToCall, "MODULE", module, "SUBTYPE", subType,
308 "VERSION", version, "LEN", data.size());
Harisuddin Mohamed Isad5c31362021-05-29 13:33:39 +0800309 return std::nullopt;
310 }
Matt Spinlerbe952d22022-07-01 11:30:11 -0500311 PyObject* pFunc = PyDict_GetItemString(pDict, funcToCall.c_str());
Harisuddin Mohamed Isad5c31362021-05-29 13:33:39 +0800312 Py_DECREF(pDict);
313 Py_INCREF(pFunc);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800314 if (PyCallable_Check(pFunc))
315 {
316 auto ud = data.data();
Matt Spinlerbe952d22022-07-01 11:30:11 -0500317 PyObject* pArgs = PyTuple_New(3);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800318 std::unique_ptr<PyObject, decltype(&pyDecRef)> argPtr(pArgs,
319 &pyDecRef);
320 PyTuple_SetItem(pArgs, 0,
321 PyLong_FromUnsignedLong((unsigned long)subType));
322 PyTuple_SetItem(pArgs, 1,
323 PyLong_FromUnsignedLong((unsigned long)version));
Matt Spinlerbe952d22022-07-01 11:30:11 -0500324 PyObject* pData = PyMemoryView_FromMemory(
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800325 reinterpret_cast<char*>(const_cast<unsigned char*>(ud)),
326 data.size(), PyBUF_READ);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800327 PyTuple_SetItem(pArgs, 2, pData);
Matt Spinlerbe952d22022-07-01 11:30:11 -0500328 PyObject* pResult = PyObject_CallObject(pFunc, pArgs);
Harisuddin Mohamed Isad5c31362021-05-29 13:33:39 +0800329 Py_DECREF(pFunc);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800330 if (pResult)
331 {
Harisuddin Mohamed Isad5c31362021-05-29 13:33:39 +0800332 std::unique_ptr<PyObject, decltype(&pyDecRef)> resPtr(
333 pResult, &pyDecRef);
Patrick Williams2544b412022-10-04 08:41:06 -0500334 PyObject* pBytes = PyUnicode_AsEncodedString(pResult, "utf-8",
335 "~E~");
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800336 std::unique_ptr<PyObject, decltype(&pyDecRef)> pyBytePtr(
337 pBytes, &pyDecRef);
338 const char* output = PyBytes_AS_STRING(pBytes);
339 try
340 {
Matt Spinlerbb1c1d52021-06-03 13:18:48 -0600341 orderedJSON json = orderedJSON::parse(output);
Harisuddin Mohamed Isad5c31362021-05-29 13:33:39 +0800342 if ((json.is_object() && !json.empty()) ||
343 (json.is_array() && json.size() > 0) ||
344 (json.is_string() && json != ""))
345 {
Matt Spinlerb832aa52023-03-21 15:32:34 -0500346 return prettyJSON(componentID, subType, version,
347 creatorID, json);
Harisuddin Mohamed Isad5c31362021-05-29 13:33:39 +0800348 }
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800349 }
Patrick Williams66491c62021-10-06 12:23:37 -0500350 catch (const std::exception& e)
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800351 {
Matt Spinler7cc3aea2023-07-07 16:24:57 -0500352 lg2::error("Bad JSON from parser. Error = {ERROR}, "
353 "module = {MODULE}, subtype = {SUBTYPE}, "
354 "version = {VERSION}, data length = {LEN}",
355 "ERROR", e, "MODULE", module, "SUBTYPE", subType,
356 "VERSION", version, "LEN", data.size());
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800357 return std::nullopt;
358 }
359 }
360 else
361 {
362 pErrStr = "No error string found";
363 PyErr_Fetch(&eType, &eValue, &eTraceback);
Harisuddin Mohamed Isad5c31362021-05-29 13:33:39 +0800364 if (eType)
365 {
366 Py_XDECREF(eType);
367 }
368 if (eTraceback)
369 {
370 Py_XDECREF(eTraceback);
371 }
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800372 if (eValue)
373 {
374 PyObject* pStr = PyObject_Str(eValue);
Harisuddin Mohamed Isad5c31362021-05-29 13:33:39 +0800375 Py_XDECREF(eValue);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800376 if (pStr)
377 {
378 pErrStr = PyUnicode_AsUTF8(pStr);
Harisuddin Mohamed Isad5c31362021-05-29 13:33:39 +0800379 Py_XDECREF(pStr);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800380 }
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800381 }
382 }
383 }
384 }
385 if (!pErrStr.empty())
386 {
Matt Spinler7cc3aea2023-07-07 16:24:57 -0500387 lg2::debug("Python exception thrown by parser. Error = {ERROR}, "
388 "module = {MODULE}, subtype = {SUBTYPE}, "
389 "version = {VERSION}, data length = {LEN}",
390 "ERROR", pErrStr, "MODULE", module, "SUBTYPE", subType,
391 "VERSION", version, "LEN", data.size());
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800392 }
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800393 return std::nullopt;
394}
395
Matt Spinleracb7c102020-01-10 13:49:22 -0600396std::optional<std::string> getJSON(uint16_t componentID, uint8_t subType,
397 uint8_t version,
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800398 const std::vector<uint8_t>& data,
Harisuddin Mohamed Isa3fdcd4e2020-08-26 11:56:42 +0800399 uint8_t creatorID,
400 const std::vector<std::string>& plugins)
Matt Spinleracb7c102020-01-10 13:49:22 -0600401{
Harisuddin Mohamed Isa3fdcd4e2020-08-26 11:56:42 +0800402 std::string subsystem = getNumberString("%c", tolower(creatorID));
403 std::string component = getNumberString("%04x", componentID);
Matt Spinleracb7c102020-01-10 13:49:22 -0600404 try
405 {
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800406 if (pv::creatorIDs.at(getNumberString("%c", creatorID)) == "BMC" &&
407 componentID == static_cast<uint16_t>(ComponentID::phosphorLogging))
Matt Spinleracb7c102020-01-10 13:49:22 -0600408 {
Matt Spinlerb832aa52023-03-21 15:32:34 -0500409 return getBuiltinFormatJSON(componentID, subType, version, data,
410 creatorID);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800411 }
Harisuddin Mohamed Isa3fdcd4e2020-08-26 11:56:42 +0800412 else if (std::find(plugins.begin(), plugins.end(),
413 subsystem + component) != plugins.end())
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800414 {
415 return getPythonJSON(componentID, subType, version, data,
416 creatorID);
Matt Spinleracb7c102020-01-10 13:49:22 -0600417 }
418 }
Patrick Williams66491c62021-10-06 12:23:37 -0500419 catch (const std::exception& e)
Matt Spinleracb7c102020-01-10 13:49:22 -0600420 {
Matt Spinler7cc3aea2023-07-07 16:24:57 -0500421 lg2::error("Failed parsing UserData. Error = {ERROR}, "
422 "component ID = {COMP_ID}, subtype = {SUBTYPE}, "
423 "version = {VERSION}, data length = {LEN}",
424 "ERROR", e, "COMP_ID", componentID, "SUBTYPE", subType,
425 "VERSION", version, "LEN", data.size());
Matt Spinleracb7c102020-01-10 13:49:22 -0600426 }
427
428 return std::nullopt;
429}
430
431} // namespace openpower::pels::user_data