1 #include "prime_inventory.hpp"
2
3 #include "event_logger.hpp"
4 #include "exceptions.hpp"
5 #include "utility/common_utility.hpp"
6 #include "utility/dbus_utility.hpp"
7 #include "utility/json_utility.hpp"
8 #include "utility/vpd_specific_utility.hpp"
9
10 #include <string>
11 #include <vector>
12
PrimeInventory()13 PrimeInventory::PrimeInventory()
14 {
15 try
16 {
17 uint16_t l_errCode = 0;
18 m_sysCfgJsonObj =
19 vpd::jsonUtility::getParsedJson(INVENTORY_JSON_SYM_LINK, l_errCode);
20
21 if (l_errCode)
22 {
23 throw std::runtime_error(
24 "JSON parsing failed for file [ " +
25 std::string(INVENTORY_JSON_SYM_LINK) +
26 " ], error : " + vpd::commonUtility::getErrCodeMsg(l_errCode));
27 }
28
29 // check for mandatory fields at this point itself.
30 if (!m_sysCfgJsonObj.contains("frus"))
31 {
32 throw std::runtime_error(
33 "Mandatory tag(s) missing from JSON file [" +
34 std::string(INVENTORY_JSON_SYM_LINK) + "]");
35 }
36
37 m_logger = vpd::Logger::getLoggerInstance();
38 }
39 catch (const std::exception& l_ex)
40 {
41 vpd::EventLogger::createSyncPel(
42 vpd::types::ErrorType::JsonFailure,
43 vpd::types::SeverityType::Critical, __FILE__, __FUNCTION__, 0,
44 "Prime inventory failed, reason: " + std::string(l_ex.what()),
45 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
46
47 throw;
48 }
49 }
50
isPrimingRequired() const51 bool PrimeInventory::isPrimingRequired() const noexcept
52 {
53 try
54 {
55 // get all object paths under PIM
56 const auto l_objectPaths = vpd::dbusUtility::GetSubTreePaths(
57 vpd::constants::systemInvPath, 0,
58 std::vector<std::string>{vpd::constants::vpdCollectionInterface});
59
60 const nlohmann::json& l_listOfFrus =
61 m_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
62
63 size_t l_invPathCount = 0;
64
65 for (const auto& l_itemFRUS : l_listOfFrus.items())
66 {
67 for (const auto& l_Fru : l_itemFRUS.value())
68 {
69 if (l_Fru.contains("ccin") || (l_Fru.contains("noprime") &&
70 l_Fru.value("noprime", false)))
71 {
72 continue;
73 }
74
75 l_invPathCount += 1;
76 }
77 }
78 return (l_objectPaths.size() < l_invPathCount);
79 }
80 catch (const std::exception& l_ex)
81 {
82 m_logger->logMessage(
83 "Error while checking is priming required or not, error: " +
84 std::string(l_ex.what()));
85 }
86
87 // In case of any error, perform priming, as it's unclear whether priming is
88 // required.
89 return true;
90 }
91
primeSystemBlueprint() const92 void PrimeInventory::primeSystemBlueprint() const noexcept
93 {
94 try
95 {
96 if (m_sysCfgJsonObj.empty() || !isPrimingRequired())
97 {
98 return;
99 }
100
101 const nlohmann::json& l_listOfFrus =
102 m_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
103
104 vpd::types::ObjectMap l_objectInterfaceMap;
105 for (const auto& l_itemFRUS : l_listOfFrus.items())
106 {
107 const std::string& l_vpdFilePath = l_itemFRUS.key();
108
109 if (l_vpdFilePath == SYSTEM_VPD_FILE_PATH)
110 {
111 continue;
112 }
113
114 // Prime the inventry for FRUs
115 for (const auto& l_Fru : m_sysCfgJsonObj["frus"][l_vpdFilePath])
116 {
117 if (!primeInventory(l_objectInterfaceMap, l_Fru))
118 {
119 m_logger->logMessage(
120 "Priming of inventory failed for FRU " +
121 std::string(l_Fru["inventoryPath"]));
122 }
123 }
124 }
125
126 // Notify PIM
127 if (!l_objectInterfaceMap.empty())
128 {
129 if (!vpd::dbusUtility::callPIM(move(l_objectInterfaceMap)))
130 {
131 m_logger->logMessage(
132 "Call to PIM failed while priming inventory");
133 }
134 }
135 else
136 {
137 m_logger->logMessage("Priming inventory failed");
138 }
139 }
140 catch (const std::exception& l_ex)
141 {
142 m_logger->logMessage("Prime system inventory failed, reason: " +
143 std::string(l_ex.what()));
144 }
145 }
146
primeInventory(vpd::types::ObjectMap & o_objectInterfaceMap,const nlohmann::json & i_fruJsonObj) const147 bool PrimeInventory::primeInventory(
148 vpd::types::ObjectMap& o_objectInterfaceMap,
149 const nlohmann::json& i_fruJsonObj) const noexcept
150 {
151 if (i_fruJsonObj.empty())
152 {
153 m_logger->logMessage("Empty FRU JSON given");
154 return false;
155 }
156
157 vpd::types::InterfaceMap l_interfaces;
158 sdbusplus::message::object_path l_fruObjectPath(
159 i_fruJsonObj["inventoryPath"]);
160
161 if (i_fruJsonObj.contains("ccin"))
162 {
163 return true;
164 }
165
166 if (i_fruJsonObj.contains("noprime") &&
167 i_fruJsonObj.value("noprime", false))
168 {
169 return true;
170 }
171
172 // Reset data under PIM for this FRU only if the FRU is not synthesized
173 // and we handle it's Present property.
174 if (isPresentPropertyHandlingRequired(i_fruJsonObj))
175 {
176 // Clear data under PIM if already exists.
177 uint16_t l_errCode = 0;
178 vpd::vpdSpecificUtility::resetDataUnderPIM(
179 std::string(i_fruJsonObj["inventoryPath"]), l_interfaces,
180 l_errCode);
181
182 if (l_errCode)
183 {
184 m_logger->logMessage(
185 "Failed to reset data under PIM for path [" +
186 std::string(i_fruJsonObj["inventoryPath"]) +
187 "], error : " + vpd::commonUtility::getErrCodeMsg(l_errCode));
188 }
189 }
190
191 // Add extra interfaces mentioned in the Json config file
192 if (i_fruJsonObj.contains("extraInterfaces"))
193 {
194 populateInterfaces(i_fruJsonObj["extraInterfaces"], l_interfaces,
195 std::monostate{});
196 }
197
198 vpd::types::PropertyMap l_propertyValueMap;
199
200 // Update Present property for this FRU only if we handle Present
201 // property for the FRU.
202 if (isPresentPropertyHandlingRequired(i_fruJsonObj))
203 {
204 l_propertyValueMap.emplace("Present", false);
205
206 // TODO: Present based on file will be taken care in future.
207 // By default present is set to false for FRU at the time of
208 // priming. Once collection goes through, it will be set to true in
209 // that flow.
210 /*if (std::filesystem::exists(i_vpdFilePath))
211 {
212 l_propertyValueMap["Present"] = true;
213 }*/
214 }
215
216 uint16_t l_errCode = 0;
217
218 vpd::vpdSpecificUtility::insertOrMerge(
219 l_interfaces, "xyz.openbmc_project.Inventory.Item",
220 move(l_propertyValueMap), l_errCode);
221
222 if (l_errCode)
223 {
224 m_logger->logMessage("Failed to insert value into map, error : " +
225 vpd::commonUtility::getErrCodeMsg(l_errCode));
226 }
227
228 if (i_fruJsonObj.value("inherit", true) &&
229 m_sysCfgJsonObj.contains("commonInterfaces"))
230 {
231 populateInterfaces(m_sysCfgJsonObj["commonInterfaces"], l_interfaces,
232 std::monostate{});
233 }
234
235 processFunctionalProperty(i_fruJsonObj["inventoryPath"], l_interfaces);
236 processEnabledProperty(i_fruJsonObj["inventoryPath"], l_interfaces);
237
238 // Emplace the default state of FRU VPD collection
239 vpd::types::PropertyMap l_fruCollectionProperty = {
240 {"Status", vpd::constants::vpdCollectionNotStarted}};
241
242 l_errCode = 0;
243 vpd::vpdSpecificUtility::insertOrMerge(
244 l_interfaces, vpd::constants::vpdCollectionInterface,
245 std::move(l_fruCollectionProperty), l_errCode);
246
247 if (l_errCode)
248 {
249 m_logger->logMessage("Failed to insert value into map, error : " +
250 vpd::commonUtility::getErrCodeMsg(l_errCode));
251 }
252
253 o_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
254 std::move(l_interfaces));
255
256 return true;
257 }
258
populateInterfaces(const nlohmann::json & i_interfaceJson,vpd::types::InterfaceMap & io_interfaceMap,const vpd::types::VPDMapVariant & i_parsedVpdMap) const259 void PrimeInventory::populateInterfaces(
260 const nlohmann::json& i_interfaceJson,
261 vpd::types::InterfaceMap& io_interfaceMap,
262 const vpd::types::VPDMapVariant& i_parsedVpdMap) const noexcept
263 {
264 for (const auto& l_interfacesPropPair : i_interfaceJson.items())
265 {
266 const std::string& l_interface = l_interfacesPropPair.key();
267 vpd::types::PropertyMap l_propertyMap;
268 uint16_t l_errCode = 0;
269
270 for (const auto& l_propValuePair : l_interfacesPropPair.value().items())
271 {
272 const std::string l_property = l_propValuePair.key();
273
274 if (l_propValuePair.value().is_boolean())
275 {
276 l_propertyMap.emplace(l_property,
277 l_propValuePair.value().get<bool>());
278 }
279 else if (l_propValuePair.value().is_string())
280 {
281 if (l_property.compare("LocationCode") == 0 &&
282 l_interface.compare("com.ibm.ipzvpd.Location") == 0)
283 {
284 std::string l_value =
285 vpd::vpdSpecificUtility::getExpandedLocationCode(
286 l_propValuePair.value().get<std::string>(),
287 i_parsedVpdMap);
288 l_propertyMap.emplace(l_property, l_value);
289
290 auto l_locCodeProperty = l_propertyMap;
291 vpd::vpdSpecificUtility::insertOrMerge(
292 io_interfaceMap,
293 std::string(vpd::constants::xyzLocationCodeInf),
294 move(l_locCodeProperty), l_errCode);
295
296 if (l_errCode)
297 {
298 m_logger->logMessage(
299 "Failed to insert value into map, error : " +
300 vpd::commonUtility::getErrCodeMsg(l_errCode));
301 }
302 }
303 else
304 {
305 l_propertyMap.emplace(
306 l_property, l_propValuePair.value().get<std::string>());
307 }
308 }
309 else if (l_propValuePair.value().is_array())
310 {
311 try
312 {
313 l_propertyMap.emplace(l_property,
314 l_propValuePair.value()
315 .get<vpd::types::BinaryVector>());
316 }
317 catch (const nlohmann::detail::type_error& e)
318 {
319 std::cerr << "Type exception: " << e.what() << "\n";
320 }
321 }
322 else if (l_propValuePair.value().is_number())
323 {
324 // For now assume the value is a size_t. In the future it would
325 // be nice to come up with a way to get the type from the JSON.
326 l_propertyMap.emplace(l_property,
327 l_propValuePair.value().get<size_t>());
328 }
329 else if (l_propValuePair.value().is_object())
330 {
331 const std::string& l_record =
332 l_propValuePair.value().value("recordName", "");
333 const std::string& l_keyword =
334 l_propValuePair.value().value("keywordName", "");
335 const std::string& l_encoding =
336 l_propValuePair.value().value("encoding", "");
337
338 uint16_t l_errCode = 0;
339
340 if (auto l_ipzVpdMap =
341 std::get_if<vpd::types::IPZVpdMap>(&i_parsedVpdMap))
342 {
343 if (!l_record.empty() && !l_keyword.empty() &&
344 (*l_ipzVpdMap).count(l_record) &&
345 (*l_ipzVpdMap).at(l_record).count(l_keyword))
346 {
347 auto l_encoded = vpd::vpdSpecificUtility::encodeKeyword(
348 ((*l_ipzVpdMap).at(l_record).at(l_keyword)),
349 l_encoding, l_errCode);
350
351 if (l_errCode)
352 {
353 m_logger->logMessage(
354 "Failed to get encoded keyword value for : " +
355 l_keyword + ", error : " +
356 vpd::commonUtility::getErrCodeMsg(l_errCode));
357 }
358
359 l_propertyMap.emplace(l_property, l_encoded);
360 }
361 }
362 else if (auto l_kwdVpdMap =
363 std::get_if<vpd::types::KeywordVpdMap>(
364 &i_parsedVpdMap))
365 {
366 if (!l_keyword.empty() && (*l_kwdVpdMap).count(l_keyword))
367 {
368 if (auto l_kwValue =
369 std::get_if<vpd::types::BinaryVector>(
370 &(*l_kwdVpdMap).at(l_keyword)))
371 {
372 auto l_encodedValue =
373 vpd::vpdSpecificUtility::encodeKeyword(
374 std::string((*l_kwValue).begin(),
375 (*l_kwValue).end()),
376 l_encoding, l_errCode);
377
378 if (l_errCode)
379 {
380 m_logger->logMessage(
381 "Failed to get encoded keyword value for : " +
382 l_keyword + ", error : " +
383 vpd::commonUtility::getErrCodeMsg(
384 l_errCode));
385 }
386
387 l_propertyMap.emplace(l_property, l_encodedValue);
388 }
389 else if (auto l_kwValue = std::get_if<std::string>(
390 &(*l_kwdVpdMap).at(l_keyword)))
391 {
392 auto l_encodedValue =
393 vpd::vpdSpecificUtility::encodeKeyword(
394 std::string((*l_kwValue).begin(),
395 (*l_kwValue).end()),
396 l_encoding, l_errCode);
397
398 if (l_errCode)
399 {
400 m_logger->logMessage(
401 "Failed to get encoded keyword value for : " +
402 l_keyword + ", error : " +
403 vpd::commonUtility::getErrCodeMsg(
404 l_errCode));
405 }
406
407 l_propertyMap.emplace(l_property, l_encodedValue);
408 }
409 else if (auto l_uintValue = std::get_if<size_t>(
410 &(*l_kwdVpdMap).at(l_keyword)))
411 {
412 l_propertyMap.emplace(l_property, *l_uintValue);
413 }
414 else
415 {
416 m_logger->logMessage(
417 "Unknown keyword found, Keywrod = " +
418 l_keyword);
419 }
420 }
421 }
422 }
423 }
424 l_errCode = 0;
425 vpd::vpdSpecificUtility::insertOrMerge(io_interfaceMap, l_interface,
426 move(l_propertyMap), l_errCode);
427
428 if (l_errCode)
429 {
430 m_logger->logMessage("Failed to insert value into map, error : " +
431 vpd::commonUtility::getErrCodeMsg(l_errCode));
432 }
433 }
434 }
435
processFunctionalProperty(const std::string & i_inventoryObjPath,vpd::types::InterfaceMap & io_interfaces) const436 void PrimeInventory::processFunctionalProperty(
437 const std::string& i_inventoryObjPath,
438 vpd::types::InterfaceMap& io_interfaces) const noexcept
439 {
440 if (!vpd::dbusUtility::isChassisPowerOn())
441 {
442 std::vector<std::string> l_operationalStatusInf{
443 vpd::constants::operationalStatusInf};
444
445 auto l_mapperObjectMap = vpd::dbusUtility::getObjectMap(
446 i_inventoryObjPath, l_operationalStatusInf);
447
448 // If the object has been found. Check if it is under PIM.
449 if (l_mapperObjectMap.size() != 0)
450 {
451 for (const auto& [l_serviceName, l_interfaceLsit] :
452 l_mapperObjectMap)
453 {
454 if (l_serviceName == vpd::constants::pimServiceName)
455 {
456 // The object is already under PIM. No need to process
457 // again. Retain the old value.
458 return;
459 }
460 }
461 }
462
463 // Implies value is not there in D-Bus. Populate it with default
464 // value "true".
465 uint16_t l_errCode = 0;
466 vpd::types::PropertyMap l_functionalProp;
467 l_functionalProp.emplace("Functional", true);
468 vpd::vpdSpecificUtility::insertOrMerge(
469 io_interfaces, vpd::constants::operationalStatusInf,
470 move(l_functionalProp), l_errCode);
471
472 if (l_errCode)
473 {
474 m_logger->logMessage("Failed to insert value into map, error : " +
475 vpd::commonUtility::getErrCodeMsg(l_errCode));
476 }
477 }
478
479 // if chassis is power on. Functional property should be there on D-Bus.
480 // Don't process.
481 return;
482 }
483
processEnabledProperty(const std::string & i_inventoryObjPath,vpd::types::InterfaceMap & io_interfaces) const484 void PrimeInventory::processEnabledProperty(
485 const std::string& i_inventoryObjPath,
486 vpd::types::InterfaceMap& io_interfaces) const noexcept
487 {
488 if (!vpd::dbusUtility::isChassisPowerOn())
489 {
490 std::vector<std::string> l_enableInf{vpd::constants::enableInf};
491
492 auto l_mapperObjectMap =
493 vpd::dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
494
495 // If the object has been found. Check if it is under PIM.
496 if (l_mapperObjectMap.size() != 0)
497 {
498 for (const auto& [l_serviceName, l_interfaceLsit] :
499 l_mapperObjectMap)
500 {
501 if (l_serviceName == vpd::constants::pimServiceName)
502 {
503 // The object is already under PIM. No need to process
504 // again. Retain the old value.
505 return;
506 }
507 }
508 }
509
510 // Implies value is not there in D-Bus. Populate it with default
511 // value "true".
512 uint16_t l_errCode = 0;
513 vpd::types::PropertyMap l_enabledProp;
514 l_enabledProp.emplace("Enabled", true);
515 vpd::vpdSpecificUtility::insertOrMerge(
516 io_interfaces, vpd::constants::enableInf, move(l_enabledProp),
517 l_errCode);
518
519 if (l_errCode)
520 {
521 m_logger->logMessage("Failed to insert value into map, error : " +
522 vpd::commonUtility::getErrCodeMsg(l_errCode));
523 }
524 }
525
526 // if chassis is power on. Enabled property should be there on D-Bus.
527 // Don't process.
528 return;
529 }
530