blob: 67a66718f2b29cd85da8efd168047a6c135c304d [file] [log] [blame]
Anupama B R445819f2025-09-18 11:00:25 -05001#include "prime_inventory.hpp"
2
3#include "event_logger.hpp"
4#include "exceptions.hpp"
Rekha Aparnac6159a22025-10-09 12:20:20 +05305#include "utility/common_utility.hpp"
Anupama B R445819f2025-09-18 11:00:25 -05006#include "utility/dbus_utility.hpp"
7#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
216 vpd::vpdSpecificUtility::insertOrMerge(
217 l_interfaces, "xyz.openbmc_project.Inventory.Item",
218 move(l_propertyValueMap));
219
220 if (i_fruJsonObj.value("inherit", true) &&
221 m_sysCfgJsonObj.contains("commonInterfaces"))
222 {
223 populateInterfaces(m_sysCfgJsonObj["commonInterfaces"], l_interfaces,
224 std::monostate{});
225 }
226
227 processFunctionalProperty(i_fruJsonObj["inventoryPath"], l_interfaces);
228 processEnabledProperty(i_fruJsonObj["inventoryPath"], l_interfaces);
229
230 // Emplace the default state of FRU VPD collection
231 vpd::types::PropertyMap l_fruCollectionProperty = {
232 {"Status", vpd::constants::vpdCollectionNotStarted}};
233
234 vpd::vpdSpecificUtility::insertOrMerge(
235 l_interfaces, vpd::constants::vpdCollectionInterface,
236 std::move(l_fruCollectionProperty));
237
238 o_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
239 std::move(l_interfaces));
240
Anupama B R445819f2025-09-18 11:00:25 -0500241 return true;
242}
243
244void PrimeInventory::populateInterfaces(
Anupama B Rca738cf2025-09-19 06:40:54 -0500245 const nlohmann::json& i_interfaceJson,
246 vpd::types::InterfaceMap& io_interfaceMap,
247 const vpd::types::VPDMapVariant& i_parsedVpdMap) const noexcept
Anupama B R445819f2025-09-18 11:00:25 -0500248{
Anupama B Rca738cf2025-09-19 06:40:54 -0500249 for (const auto& l_interfacesPropPair : i_interfaceJson.items())
250 {
251 const std::string& l_interface = l_interfacesPropPair.key();
252 vpd::types::PropertyMap l_propertyMap;
253
254 for (const auto& l_propValuePair : l_interfacesPropPair.value().items())
255 {
256 const std::string l_property = l_propValuePair.key();
257
258 if (l_propValuePair.value().is_boolean())
259 {
260 l_propertyMap.emplace(l_property,
261 l_propValuePair.value().get<bool>());
262 }
263 else if (l_propValuePair.value().is_string())
264 {
265 if (l_property.compare("LocationCode") == 0 &&
266 l_interface.compare("com.ibm.ipzvpd.Location") == 0)
267 {
268 std::string l_value =
269 vpd::vpdSpecificUtility::getExpandedLocationCode(
270 l_propValuePair.value().get<std::string>(),
271 i_parsedVpdMap);
272 l_propertyMap.emplace(l_property, l_value);
273
274 auto l_locCodeProperty = l_propertyMap;
275 vpd::vpdSpecificUtility::insertOrMerge(
276 io_interfaceMap,
277 std::string(vpd::constants::xyzLocationCodeInf),
278 move(l_locCodeProperty));
279 }
280 else
281 {
282 l_propertyMap.emplace(
283 l_property, l_propValuePair.value().get<std::string>());
284 }
285 }
286 else if (l_propValuePair.value().is_array())
287 {
288 try
289 {
290 l_propertyMap.emplace(l_property,
291 l_propValuePair.value()
292 .get<vpd::types::BinaryVector>());
293 }
294 catch (const nlohmann::detail::type_error& e)
295 {
296 std::cerr << "Type exception: " << e.what() << "\n";
297 }
298 }
299 else if (l_propValuePair.value().is_number())
300 {
301 // For now assume the value is a size_t. In the future it would
302 // be nice to come up with a way to get the type from the JSON.
303 l_propertyMap.emplace(l_property,
304 l_propValuePair.value().get<size_t>());
305 }
306 else if (l_propValuePair.value().is_object())
307 {
308 const std::string& l_record =
309 l_propValuePair.value().value("recordName", "");
310 const std::string& l_keyword =
311 l_propValuePair.value().value("keywordName", "");
312 const std::string& l_encoding =
313 l_propValuePair.value().value("encoding", "");
314
Rekha Aparna17ddfb52025-10-09 20:45:53 -0500315 uint16_t l_errCode = 0;
316
Anupama B Rca738cf2025-09-19 06:40:54 -0500317 if (auto l_ipzVpdMap =
318 std::get_if<vpd::types::IPZVpdMap>(&i_parsedVpdMap))
319 {
320 if (!l_record.empty() && !l_keyword.empty() &&
321 (*l_ipzVpdMap).count(l_record) &&
322 (*l_ipzVpdMap).at(l_record).count(l_keyword))
323 {
324 auto l_encoded = vpd::vpdSpecificUtility::encodeKeyword(
325 ((*l_ipzVpdMap).at(l_record).at(l_keyword)),
Rekha Aparna17ddfb52025-10-09 20:45:53 -0500326 l_encoding, l_errCode);
327
328 if (l_errCode)
329 {
330 m_logger->logMessage(
331 "Failed to get encoded keyword value for : " +
332 l_keyword + ", error : " +
333 vpd::commonUtility::getErrCodeMsg(l_errCode));
334 }
335
Anupama B Rca738cf2025-09-19 06:40:54 -0500336 l_propertyMap.emplace(l_property, l_encoded);
337 }
338 }
339 else if (auto l_kwdVpdMap =
340 std::get_if<vpd::types::KeywordVpdMap>(
341 &i_parsedVpdMap))
342 {
343 if (!l_keyword.empty() && (*l_kwdVpdMap).count(l_keyword))
344 {
345 if (auto l_kwValue =
346 std::get_if<vpd::types::BinaryVector>(
347 &(*l_kwdVpdMap).at(l_keyword)))
348 {
349 auto l_encodedValue =
350 vpd::vpdSpecificUtility::encodeKeyword(
351 std::string((*l_kwValue).begin(),
352 (*l_kwValue).end()),
Rekha Aparna17ddfb52025-10-09 20:45:53 -0500353 l_encoding, l_errCode);
354
355 if (l_errCode)
356 {
357 m_logger->logMessage(
358 "Failed to get encoded keyword value for : " +
359 l_keyword + ", error : " +
360 vpd::commonUtility::getErrCodeMsg(
361 l_errCode));
362 }
Anupama B Rca738cf2025-09-19 06:40:54 -0500363
364 l_propertyMap.emplace(l_property, l_encodedValue);
365 }
366 else if (auto l_kwValue = std::get_if<std::string>(
367 &(*l_kwdVpdMap).at(l_keyword)))
368 {
369 auto l_encodedValue =
370 vpd::vpdSpecificUtility::encodeKeyword(
371 std::string((*l_kwValue).begin(),
372 (*l_kwValue).end()),
Rekha Aparna17ddfb52025-10-09 20:45:53 -0500373 l_encoding, l_errCode);
374
375 if (l_errCode)
376 {
377 m_logger->logMessage(
378 "Failed to get encoded keyword value for : " +
379 l_keyword + ", error : " +
380 vpd::commonUtility::getErrCodeMsg(
381 l_errCode));
382 }
Anupama B Rca738cf2025-09-19 06:40:54 -0500383
384 l_propertyMap.emplace(l_property, l_encodedValue);
385 }
386 else if (auto l_uintValue = std::get_if<size_t>(
387 &(*l_kwdVpdMap).at(l_keyword)))
388 {
389 l_propertyMap.emplace(l_property, *l_uintValue);
390 }
391 else
392 {
393 m_logger->logMessage(
394 "Unknown keyword found, Keywrod = " +
395 l_keyword);
396 }
397 }
398 }
399 }
400 }
401 vpd::vpdSpecificUtility::insertOrMerge(io_interfaceMap, l_interface,
402 move(l_propertyMap));
403 }
Anupama B R445819f2025-09-18 11:00:25 -0500404}
405
406void PrimeInventory::processFunctionalProperty(
Anupama B Rca738cf2025-09-19 06:40:54 -0500407 const std::string& i_inventoryObjPath,
408 vpd::types::InterfaceMap& io_interfaces) const noexcept
Anupama B R445819f2025-09-18 11:00:25 -0500409{
Anupama B Rca738cf2025-09-19 06:40:54 -0500410 if (!vpd::dbusUtility::isChassisPowerOn())
411 {
412 std::vector<std::string> l_operationalStatusInf{
413 vpd::constants::operationalStatusInf};
414
415 auto l_mapperObjectMap = vpd::dbusUtility::getObjectMap(
416 i_inventoryObjPath, l_operationalStatusInf);
417
418 // If the object has been found. Check if it is under PIM.
419 if (l_mapperObjectMap.size() != 0)
420 {
421 for (const auto& [l_serviceName, l_interfaceLsit] :
422 l_mapperObjectMap)
423 {
424 if (l_serviceName == vpd::constants::pimServiceName)
425 {
426 // The object is already under PIM. No need to process
427 // again. Retain the old value.
428 return;
429 }
430 }
431 }
432
433 // Implies value is not there in D-Bus. Populate it with default
434 // value "true".
435 vpd::types::PropertyMap l_functionalProp;
436 l_functionalProp.emplace("Functional", true);
437 vpd::vpdSpecificUtility::insertOrMerge(
438 io_interfaces, vpd::constants::operationalStatusInf,
439 move(l_functionalProp));
440 }
441
442 // if chassis is power on. Functional property should be there on D-Bus.
443 // Don't process.
444 return;
Anupama B R445819f2025-09-18 11:00:25 -0500445}
446
447void PrimeInventory::processEnabledProperty(
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_enableInf{vpd::constants::enableInf};
454
455 auto l_mapperObjectMap =
456 vpd::dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
457
458 // If the object has been found. Check if it is under PIM.
459 if (l_mapperObjectMap.size() != 0)
460 {
461 for (const auto& [l_serviceName, l_interfaceLsit] :
462 l_mapperObjectMap)
463 {
464 if (l_serviceName == vpd::constants::pimServiceName)
465 {
466 // The object is already under PIM. No need to process
467 // again. Retain the old value.
468 return;
469 }
470 }
471 }
472
473 // Implies value is not there in D-Bus. Populate it with default
474 // value "true".
475 vpd::types::PropertyMap l_enabledProp;
476 l_enabledProp.emplace("Enabled", true);
477 vpd::vpdSpecificUtility::insertOrMerge(
478 io_interfaces, vpd::constants::enableInf, move(l_enabledProp));
479 }
480
481 // if chassis is power on. Enabled property should be there on D-Bus.
482 // Don't process.
483 return;
Anupama B R445819f2025-09-18 11:00:25 -0500484}