blob: ad06f1629883e560db93905b1a013a78af524cf5 [file] [log] [blame]
Anupama B R445819f2025-09-18 11:00:25 -05001#include "prime_inventory.hpp"
2
Anupama B R445819f2025-09-18 11:00:25 -05003#include "exceptions.hpp"
Rekha Aparnac6159a22025-10-09 12:20:20 +05304#include "utility/common_utility.hpp"
Anupama B R445819f2025-09-18 11:00:25 -05005#include "utility/dbus_utility.hpp"
Rekha Aparnaa39aafa2025-11-04 00:06:12 -06006#include "utility/event_logger_utility.hpp"
Anupama B R445819f2025-09-18 11:00:25 -05007#include "utility/json_utility.hpp"
8#include "utility/vpd_specific_utility.hpp"
9
10#include <string>
Anupama B Rca738cf2025-09-19 06:40:54 -050011#include <vector>
Anupama B R445819f2025-09-18 11:00:25 -050012
13PrimeInventory::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 [ " +
Rekha Aparnac6159a22025-10-09 12:20:20 +053025 std::string(INVENTORY_JSON_SYM_LINK) +
26 " ], error : " + vpd::commonUtility::getErrCodeMsg(l_errCode));
Anupama B R445819f2025-09-18 11:00:25 -050027 }
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 }
Anupama B Rca738cf2025-09-19 06:40:54 -050036
37 m_logger = vpd::Logger::getLoggerInstance();
Anupama B R445819f2025-09-18 11:00:25 -050038 }
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
51bool PrimeInventory::isPrimingRequired() const noexcept
52{
Anupama B Rca738cf2025-09-19 06:40:54 -050053 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 }
Anupama B R61741a42025-10-16 00:31:26 -050078 return (l_objectPaths.size() < l_invPathCount);
Anupama B Rca738cf2025-09-19 06:40:54 -050079 }
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.
Anupama B R445819f2025-09-18 11:00:25 -050089 return true;
90}
91
92void PrimeInventory::primeSystemBlueprint() const noexcept
93{
94 try
95 {
Anupama B R61741a42025-10-16 00:31:26 -050096 if (m_sysCfgJsonObj.empty() || !isPrimingRequired())
Anupama B Rca738cf2025-09-19 06:40:54 -050097 {
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 }
Anupama B R445819f2025-09-18 11:00:25 -0500139 }
140 catch (const std::exception& l_ex)
141 {
Anupama B Rca738cf2025-09-19 06:40:54 -0500142 m_logger->logMessage("Prime system inventory failed, reason: " +
143 std::string(l_ex.what()));
Anupama B R445819f2025-09-18 11:00:25 -0500144 }
145}
146
147bool PrimeInventory::primeInventory(
Anupama B Rca738cf2025-09-19 06:40:54 -0500148 vpd::types::ObjectMap& o_objectInterfaceMap,
149 const nlohmann::json& i_fruJsonObj) const noexcept
Anupama B R445819f2025-09-18 11:00:25 -0500150{
Anupama B Rca738cf2025-09-19 06:40:54 -0500151 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.
Rekha Aparna2c04eeb2025-10-22 22:15:07 -0500177 uint16_t l_errCode = 0;
Anupama B Rca738cf2025-09-19 06:40:54 -0500178 vpd::vpdSpecificUtility::resetDataUnderPIM(
Rekha Aparna2c04eeb2025-10-22 22:15:07 -0500179 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 }
Anupama B Rca738cf2025-09-19 06:40:54 -0500189 }
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
Rekha Aparnad3662222025-10-14 10:33:17 -0500216 uint16_t l_errCode = 0;
217
Anupama B Rca738cf2025-09-19 06:40:54 -0500218 vpd::vpdSpecificUtility::insertOrMerge(
219 l_interfaces, "xyz.openbmc_project.Inventory.Item",
Rekha Aparnad3662222025-10-14 10:33:17 -0500220 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 }
Anupama B Rca738cf2025-09-19 06:40:54 -0500227
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 vpd::vpdSpecificUtility::insertOrMerge(
243 l_interfaces, vpd::constants::vpdCollectionInterface,
Rekha Aparnad3662222025-10-14 10:33:17 -0500244 std::move(l_fruCollectionProperty), l_errCode);
245
246 if (l_errCode)
247 {
248 m_logger->logMessage("Failed to insert value into map, error : " +
249 vpd::commonUtility::getErrCodeMsg(l_errCode));
250 }
Anupama B Rca738cf2025-09-19 06:40:54 -0500251
252 o_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
253 std::move(l_interfaces));
254
Anupama B R445819f2025-09-18 11:00:25 -0500255 return true;
256}
257
258void PrimeInventory::populateInterfaces(
Anupama B Rca738cf2025-09-19 06:40:54 -0500259 const nlohmann::json& i_interfaceJson,
260 vpd::types::InterfaceMap& io_interfaceMap,
261 const vpd::types::VPDMapVariant& i_parsedVpdMap) const noexcept
Anupama B R445819f2025-09-18 11:00:25 -0500262{
Souvik Roy0f740562025-10-27 14:43:33 +0000263 if (i_interfaceJson.empty())
264 {
265 return;
266 }
267
Anupama B Rca738cf2025-09-19 06:40:54 -0500268 for (const auto& l_interfacesPropPair : i_interfaceJson.items())
269 {
270 const std::string& l_interface = l_interfacesPropPair.key();
271 vpd::types::PropertyMap l_propertyMap;
Rekha Aparnad3662222025-10-14 10:33:17 -0500272 uint16_t l_errCode = 0;
Anupama B Rca738cf2025-09-19 06:40:54 -0500273
274 for (const auto& l_propValuePair : l_interfacesPropPair.value().items())
275 {
276 const std::string l_property = l_propValuePair.key();
277
278 if (l_propValuePair.value().is_boolean())
279 {
280 l_propertyMap.emplace(l_property,
281 l_propValuePair.value().get<bool>());
282 }
283 else if (l_propValuePair.value().is_string())
284 {
285 if (l_property.compare("LocationCode") == 0 &&
286 l_interface.compare("com.ibm.ipzvpd.Location") == 0)
287 {
288 std::string l_value =
289 vpd::vpdSpecificUtility::getExpandedLocationCode(
290 l_propValuePair.value().get<std::string>(),
Rekha Aparna6256db92025-10-17 09:58:49 -0500291 i_parsedVpdMap, l_errCode);
292
293 if (l_errCode)
294 {
295 m_logger->logMessage(
296 "Failed to get expanded location code for location code - " +
297 l_propValuePair.value().get<std::string>() +
298 " ,error : " +
299 vpd::commonUtility::getErrCodeMsg(l_errCode));
Rekha Aparna6256db92025-10-17 09:58:49 -0500300 }
301
Anupama B Rca738cf2025-09-19 06:40:54 -0500302 l_propertyMap.emplace(l_property, l_value);
303
304 auto l_locCodeProperty = l_propertyMap;
305 vpd::vpdSpecificUtility::insertOrMerge(
306 io_interfaceMap,
307 std::string(vpd::constants::xyzLocationCodeInf),
Rekha Aparnad3662222025-10-14 10:33:17 -0500308 move(l_locCodeProperty), l_errCode);
309
310 if (l_errCode)
311 {
312 m_logger->logMessage(
313 "Failed to insert value into map, error : " +
314 vpd::commonUtility::getErrCodeMsg(l_errCode));
315 }
Anupama B Rca738cf2025-09-19 06:40:54 -0500316 }
317 else
318 {
319 l_propertyMap.emplace(
320 l_property, l_propValuePair.value().get<std::string>());
321 }
322 }
323 else if (l_propValuePair.value().is_array())
324 {
325 try
326 {
327 l_propertyMap.emplace(l_property,
328 l_propValuePair.value()
329 .get<vpd::types::BinaryVector>());
330 }
331 catch (const nlohmann::detail::type_error& e)
332 {
333 std::cerr << "Type exception: " << e.what() << "\n";
334 }
335 }
336 else if (l_propValuePair.value().is_number())
337 {
338 // For now assume the value is a size_t. In the future it would
339 // be nice to come up with a way to get the type from the JSON.
340 l_propertyMap.emplace(l_property,
341 l_propValuePair.value().get<size_t>());
342 }
343 else if (l_propValuePair.value().is_object())
344 {
345 const std::string& l_record =
346 l_propValuePair.value().value("recordName", "");
347 const std::string& l_keyword =
348 l_propValuePair.value().value("keywordName", "");
349 const std::string& l_encoding =
350 l_propValuePair.value().value("encoding", "");
351
352 if (auto l_ipzVpdMap =
353 std::get_if<vpd::types::IPZVpdMap>(&i_parsedVpdMap))
354 {
355 if (!l_record.empty() && !l_keyword.empty() &&
356 (*l_ipzVpdMap).count(l_record) &&
357 (*l_ipzVpdMap).at(l_record).count(l_keyword))
358 {
359 auto l_encoded = vpd::vpdSpecificUtility::encodeKeyword(
360 ((*l_ipzVpdMap).at(l_record).at(l_keyword)),
Rekha Aparna17ddfb52025-10-09 20:45:53 -0500361 l_encoding, l_errCode);
362
363 if (l_errCode)
364 {
365 m_logger->logMessage(
366 "Failed to get encoded keyword value for : " +
367 l_keyword + ", error : " +
368 vpd::commonUtility::getErrCodeMsg(l_errCode));
369 }
370
Anupama B Rca738cf2025-09-19 06:40:54 -0500371 l_propertyMap.emplace(l_property, l_encoded);
372 }
373 }
374 else if (auto l_kwdVpdMap =
375 std::get_if<vpd::types::KeywordVpdMap>(
376 &i_parsedVpdMap))
377 {
378 if (!l_keyword.empty() && (*l_kwdVpdMap).count(l_keyword))
379 {
380 if (auto l_kwValue =
381 std::get_if<vpd::types::BinaryVector>(
382 &(*l_kwdVpdMap).at(l_keyword)))
383 {
384 auto l_encodedValue =
385 vpd::vpdSpecificUtility::encodeKeyword(
386 std::string((*l_kwValue).begin(),
387 (*l_kwValue).end()),
Rekha Aparna17ddfb52025-10-09 20:45:53 -0500388 l_encoding, l_errCode);
389
390 if (l_errCode)
391 {
392 m_logger->logMessage(
393 "Failed to get encoded keyword value for : " +
394 l_keyword + ", error : " +
395 vpd::commonUtility::getErrCodeMsg(
396 l_errCode));
397 }
Anupama B Rca738cf2025-09-19 06:40:54 -0500398
399 l_propertyMap.emplace(l_property, l_encodedValue);
400 }
401 else if (auto l_kwValue = std::get_if<std::string>(
402 &(*l_kwdVpdMap).at(l_keyword)))
403 {
404 auto l_encodedValue =
405 vpd::vpdSpecificUtility::encodeKeyword(
406 std::string((*l_kwValue).begin(),
407 (*l_kwValue).end()),
Rekha Aparna17ddfb52025-10-09 20:45:53 -0500408 l_encoding, l_errCode);
409
410 if (l_errCode)
411 {
412 m_logger->logMessage(
413 "Failed to get encoded keyword value for : " +
414 l_keyword + ", error : " +
415 vpd::commonUtility::getErrCodeMsg(
416 l_errCode));
417 }
Anupama B Rca738cf2025-09-19 06:40:54 -0500418
419 l_propertyMap.emplace(l_property, l_encodedValue);
420 }
421 else if (auto l_uintValue = std::get_if<size_t>(
422 &(*l_kwdVpdMap).at(l_keyword)))
423 {
424 l_propertyMap.emplace(l_property, *l_uintValue);
425 }
426 else
427 {
428 m_logger->logMessage(
429 "Unknown keyword found, Keywrod = " +
430 l_keyword);
431 }
432 }
433 }
434 }
435 }
436 vpd::vpdSpecificUtility::insertOrMerge(io_interfaceMap, l_interface,
Rekha Aparnad3662222025-10-14 10:33:17 -0500437 move(l_propertyMap), l_errCode);
438
439 if (l_errCode)
440 {
441 m_logger->logMessage("Failed to insert value into map, error : " +
442 vpd::commonUtility::getErrCodeMsg(l_errCode));
443 }
Anupama B Rca738cf2025-09-19 06:40:54 -0500444 }
Anupama B R445819f2025-09-18 11:00:25 -0500445}
446
447void PrimeInventory::processFunctionalProperty(
Anupama B Rca738cf2025-09-19 06:40:54 -0500448 const std::string& i_inventoryObjPath,
449 vpd::types::InterfaceMap& io_interfaces) const noexcept
Anupama B R445819f2025-09-18 11:00:25 -0500450{
Anupama B Rca738cf2025-09-19 06:40:54 -0500451 if (!vpd::dbusUtility::isChassisPowerOn())
452 {
453 std::vector<std::string> l_operationalStatusInf{
454 vpd::constants::operationalStatusInf};
455
456 auto l_mapperObjectMap = vpd::dbusUtility::getObjectMap(
457 i_inventoryObjPath, l_operationalStatusInf);
458
459 // If the object has been found. Check if it is under PIM.
460 if (l_mapperObjectMap.size() != 0)
461 {
462 for (const auto& [l_serviceName, l_interfaceLsit] :
463 l_mapperObjectMap)
464 {
465 if (l_serviceName == vpd::constants::pimServiceName)
466 {
467 // The object is already under PIM. No need to process
468 // again. Retain the old value.
469 return;
470 }
471 }
472 }
473
474 // Implies value is not there in D-Bus. Populate it with default
475 // value "true".
Rekha Aparnad3662222025-10-14 10:33:17 -0500476 uint16_t l_errCode = 0;
Anupama B Rca738cf2025-09-19 06:40:54 -0500477 vpd::types::PropertyMap l_functionalProp;
478 l_functionalProp.emplace("Functional", true);
479 vpd::vpdSpecificUtility::insertOrMerge(
480 io_interfaces, vpd::constants::operationalStatusInf,
Rekha Aparnad3662222025-10-14 10:33:17 -0500481 move(l_functionalProp), l_errCode);
482
483 if (l_errCode)
484 {
485 m_logger->logMessage("Failed to insert value into map, error : " +
486 vpd::commonUtility::getErrCodeMsg(l_errCode));
487 }
Anupama B Rca738cf2025-09-19 06:40:54 -0500488 }
489
490 // if chassis is power on. Functional property should be there on D-Bus.
491 // Don't process.
492 return;
Anupama B R445819f2025-09-18 11:00:25 -0500493}
494
495void PrimeInventory::processEnabledProperty(
Anupama B Rca738cf2025-09-19 06:40:54 -0500496 const std::string& i_inventoryObjPath,
497 vpd::types::InterfaceMap& io_interfaces) const noexcept
Anupama B R445819f2025-09-18 11:00:25 -0500498{
Anupama B Rca738cf2025-09-19 06:40:54 -0500499 if (!vpd::dbusUtility::isChassisPowerOn())
500 {
501 std::vector<std::string> l_enableInf{vpd::constants::enableInf};
502
503 auto l_mapperObjectMap =
504 vpd::dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
505
506 // If the object has been found. Check if it is under PIM.
507 if (l_mapperObjectMap.size() != 0)
508 {
509 for (const auto& [l_serviceName, l_interfaceLsit] :
510 l_mapperObjectMap)
511 {
512 if (l_serviceName == vpd::constants::pimServiceName)
513 {
514 // The object is already under PIM. No need to process
515 // again. Retain the old value.
516 return;
517 }
518 }
519 }
520
521 // Implies value is not there in D-Bus. Populate it with default
522 // value "true".
Rekha Aparnad3662222025-10-14 10:33:17 -0500523 uint16_t l_errCode = 0;
Anupama B Rca738cf2025-09-19 06:40:54 -0500524 vpd::types::PropertyMap l_enabledProp;
525 l_enabledProp.emplace("Enabled", true);
526 vpd::vpdSpecificUtility::insertOrMerge(
Rekha Aparnad3662222025-10-14 10:33:17 -0500527 io_interfaces, vpd::constants::enableInf, move(l_enabledProp),
528 l_errCode);
529
530 if (l_errCode)
531 {
532 m_logger->logMessage("Failed to insert value into map, error : " +
533 vpd::commonUtility::getErrCodeMsg(l_errCode));
534 }
Anupama B Rca738cf2025-09-19 06:40:54 -0500535 }
536
537 // if chassis is power on. Enabled property should be there on D-Bus.
538 // Don't process.
539 return;
Anupama B R445819f2025-09-18 11:00:25 -0500540}