xref: /openbmc/openpower-vpd-parser/wait-vpd-parser/src/prime_inventory.cpp (revision a39aafa337352262596e07577fdf88cc294284c9)
1 #include "prime_inventory.hpp"
2 
3 #include "exceptions.hpp"
4 #include "utility/common_utility.hpp"
5 #include "utility/dbus_utility.hpp"
6 #include "utility/event_logger_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     vpd::vpdSpecificUtility::insertOrMerge(
243         l_interfaces, vpd::constants::vpdCollectionInterface,
244         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     }
251 
252     o_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
253                                  std::move(l_interfaces));
254 
255     return true;
256 }
257 
populateInterfaces(const nlohmann::json & i_interfaceJson,vpd::types::InterfaceMap & io_interfaceMap,const vpd::types::VPDMapVariant & i_parsedVpdMap) const258 void PrimeInventory::populateInterfaces(
259     const nlohmann::json& i_interfaceJson,
260     vpd::types::InterfaceMap& io_interfaceMap,
261     const vpd::types::VPDMapVariant& i_parsedVpdMap) const noexcept
262 {
263     if (i_interfaceJson.empty())
264     {
265         return;
266     }
267 
268     for (const auto& l_interfacesPropPair : i_interfaceJson.items())
269     {
270         const std::string& l_interface = l_interfacesPropPair.key();
271         vpd::types::PropertyMap l_propertyMap;
272         uint16_t l_errCode = 0;
273 
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>(),
291                             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));
300                     }
301 
302                     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),
308                         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                     }
316                 }
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)),
361                             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 
371                         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()),
388                                     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                             }
398 
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()),
408                                     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                             }
418 
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,
437                                                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         }
444     }
445 }
446 
processFunctionalProperty(const std::string & i_inventoryObjPath,vpd::types::InterfaceMap & io_interfaces) const447 void PrimeInventory::processFunctionalProperty(
448     const std::string& i_inventoryObjPath,
449     vpd::types::InterfaceMap& io_interfaces) const noexcept
450 {
451     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".
476         uint16_t l_errCode = 0;
477         vpd::types::PropertyMap l_functionalProp;
478         l_functionalProp.emplace("Functional", true);
479         vpd::vpdSpecificUtility::insertOrMerge(
480             io_interfaces, vpd::constants::operationalStatusInf,
481             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         }
488     }
489 
490     // if chassis is power on. Functional property should be there on D-Bus.
491     // Don't process.
492     return;
493 }
494 
processEnabledProperty(const std::string & i_inventoryObjPath,vpd::types::InterfaceMap & io_interfaces) const495 void PrimeInventory::processEnabledProperty(
496     const std::string& i_inventoryObjPath,
497     vpd::types::InterfaceMap& io_interfaces) const noexcept
498 {
499     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".
523         uint16_t l_errCode = 0;
524         vpd::types::PropertyMap l_enabledProp;
525         l_enabledProp.emplace("Enabled", true);
526         vpd::vpdSpecificUtility::insertOrMerge(
527             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         }
535     }
536 
537     // if chassis is power on. Enabled property should be there on D-Bus.
538     // Don't process.
539     return;
540 }
541