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 vpd::vpdSpecificUtility::resetDataUnderPIM(
178 std::string(i_fruJsonObj["inventoryPath"]), l_interfaces);
179 }
180
181 // Add extra interfaces mentioned in the Json config file
182 if (i_fruJsonObj.contains("extraInterfaces"))
183 {
184 populateInterfaces(i_fruJsonObj["extraInterfaces"], l_interfaces,
185 std::monostate{});
186 }
187
188 vpd::types::PropertyMap l_propertyValueMap;
189
190 // Update Present property for this FRU only if we handle Present
191 // property for the FRU.
192 if (isPresentPropertyHandlingRequired(i_fruJsonObj))
193 {
194 l_propertyValueMap.emplace("Present", false);
195
196 // TODO: Present based on file will be taken care in future.
197 // By default present is set to false for FRU at the time of
198 // priming. Once collection goes through, it will be set to true in
199 // that flow.
200 /*if (std::filesystem::exists(i_vpdFilePath))
201 {
202 l_propertyValueMap["Present"] = true;
203 }*/
204 }
205
206 vpd::vpdSpecificUtility::insertOrMerge(
207 l_interfaces, "xyz.openbmc_project.Inventory.Item",
208 move(l_propertyValueMap));
209
210 if (i_fruJsonObj.value("inherit", true) &&
211 m_sysCfgJsonObj.contains("commonInterfaces"))
212 {
213 populateInterfaces(m_sysCfgJsonObj["commonInterfaces"], l_interfaces,
214 std::monostate{});
215 }
216
217 processFunctionalProperty(i_fruJsonObj["inventoryPath"], l_interfaces);
218 processEnabledProperty(i_fruJsonObj["inventoryPath"], l_interfaces);
219
220 // Emplace the default state of FRU VPD collection
221 vpd::types::PropertyMap l_fruCollectionProperty = {
222 {"Status", vpd::constants::vpdCollectionNotStarted}};
223
224 vpd::vpdSpecificUtility::insertOrMerge(
225 l_interfaces, vpd::constants::vpdCollectionInterface,
226 std::move(l_fruCollectionProperty));
227
228 o_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
229 std::move(l_interfaces));
230
231 return true;
232 }
233
populateInterfaces(const nlohmann::json & i_interfaceJson,vpd::types::InterfaceMap & io_interfaceMap,const vpd::types::VPDMapVariant & i_parsedVpdMap) const234 void PrimeInventory::populateInterfaces(
235 const nlohmann::json& i_interfaceJson,
236 vpd::types::InterfaceMap& io_interfaceMap,
237 const vpd::types::VPDMapVariant& i_parsedVpdMap) const noexcept
238 {
239 for (const auto& l_interfacesPropPair : i_interfaceJson.items())
240 {
241 const std::string& l_interface = l_interfacesPropPair.key();
242 vpd::types::PropertyMap l_propertyMap;
243
244 for (const auto& l_propValuePair : l_interfacesPropPair.value().items())
245 {
246 const std::string l_property = l_propValuePair.key();
247
248 if (l_propValuePair.value().is_boolean())
249 {
250 l_propertyMap.emplace(l_property,
251 l_propValuePair.value().get<bool>());
252 }
253 else if (l_propValuePair.value().is_string())
254 {
255 if (l_property.compare("LocationCode") == 0 &&
256 l_interface.compare("com.ibm.ipzvpd.Location") == 0)
257 {
258 std::string l_value =
259 vpd::vpdSpecificUtility::getExpandedLocationCode(
260 l_propValuePair.value().get<std::string>(),
261 i_parsedVpdMap);
262 l_propertyMap.emplace(l_property, l_value);
263
264 auto l_locCodeProperty = l_propertyMap;
265 vpd::vpdSpecificUtility::insertOrMerge(
266 io_interfaceMap,
267 std::string(vpd::constants::xyzLocationCodeInf),
268 move(l_locCodeProperty));
269 }
270 else
271 {
272 l_propertyMap.emplace(
273 l_property, l_propValuePair.value().get<std::string>());
274 }
275 }
276 else if (l_propValuePair.value().is_array())
277 {
278 try
279 {
280 l_propertyMap.emplace(l_property,
281 l_propValuePair.value()
282 .get<vpd::types::BinaryVector>());
283 }
284 catch (const nlohmann::detail::type_error& e)
285 {
286 std::cerr << "Type exception: " << e.what() << "\n";
287 }
288 }
289 else if (l_propValuePair.value().is_number())
290 {
291 // For now assume the value is a size_t. In the future it would
292 // be nice to come up with a way to get the type from the JSON.
293 l_propertyMap.emplace(l_property,
294 l_propValuePair.value().get<size_t>());
295 }
296 else if (l_propValuePair.value().is_object())
297 {
298 const std::string& l_record =
299 l_propValuePair.value().value("recordName", "");
300 const std::string& l_keyword =
301 l_propValuePair.value().value("keywordName", "");
302 const std::string& l_encoding =
303 l_propValuePair.value().value("encoding", "");
304
305 if (auto l_ipzVpdMap =
306 std::get_if<vpd::types::IPZVpdMap>(&i_parsedVpdMap))
307 {
308 if (!l_record.empty() && !l_keyword.empty() &&
309 (*l_ipzVpdMap).count(l_record) &&
310 (*l_ipzVpdMap).at(l_record).count(l_keyword))
311 {
312 auto l_encoded = vpd::vpdSpecificUtility::encodeKeyword(
313 ((*l_ipzVpdMap).at(l_record).at(l_keyword)),
314 l_encoding);
315 l_propertyMap.emplace(l_property, l_encoded);
316 }
317 }
318 else if (auto l_kwdVpdMap =
319 std::get_if<vpd::types::KeywordVpdMap>(
320 &i_parsedVpdMap))
321 {
322 if (!l_keyword.empty() && (*l_kwdVpdMap).count(l_keyword))
323 {
324 if (auto l_kwValue =
325 std::get_if<vpd::types::BinaryVector>(
326 &(*l_kwdVpdMap).at(l_keyword)))
327 {
328 auto l_encodedValue =
329 vpd::vpdSpecificUtility::encodeKeyword(
330 std::string((*l_kwValue).begin(),
331 (*l_kwValue).end()),
332 l_encoding);
333
334 l_propertyMap.emplace(l_property, l_encodedValue);
335 }
336 else if (auto l_kwValue = std::get_if<std::string>(
337 &(*l_kwdVpdMap).at(l_keyword)))
338 {
339 auto l_encodedValue =
340 vpd::vpdSpecificUtility::encodeKeyword(
341 std::string((*l_kwValue).begin(),
342 (*l_kwValue).end()),
343 l_encoding);
344
345 l_propertyMap.emplace(l_property, l_encodedValue);
346 }
347 else if (auto l_uintValue = std::get_if<size_t>(
348 &(*l_kwdVpdMap).at(l_keyword)))
349 {
350 l_propertyMap.emplace(l_property, *l_uintValue);
351 }
352 else
353 {
354 m_logger->logMessage(
355 "Unknown keyword found, Keywrod = " +
356 l_keyword);
357 }
358 }
359 }
360 }
361 }
362 vpd::vpdSpecificUtility::insertOrMerge(io_interfaceMap, l_interface,
363 move(l_propertyMap));
364 }
365 }
366
processFunctionalProperty(const std::string & i_inventoryObjPath,vpd::types::InterfaceMap & io_interfaces) const367 void PrimeInventory::processFunctionalProperty(
368 const std::string& i_inventoryObjPath,
369 vpd::types::InterfaceMap& io_interfaces) const noexcept
370 {
371 if (!vpd::dbusUtility::isChassisPowerOn())
372 {
373 std::vector<std::string> l_operationalStatusInf{
374 vpd::constants::operationalStatusInf};
375
376 auto l_mapperObjectMap = vpd::dbusUtility::getObjectMap(
377 i_inventoryObjPath, l_operationalStatusInf);
378
379 // If the object has been found. Check if it is under PIM.
380 if (l_mapperObjectMap.size() != 0)
381 {
382 for (const auto& [l_serviceName, l_interfaceLsit] :
383 l_mapperObjectMap)
384 {
385 if (l_serviceName == vpd::constants::pimServiceName)
386 {
387 // The object is already under PIM. No need to process
388 // again. Retain the old value.
389 return;
390 }
391 }
392 }
393
394 // Implies value is not there in D-Bus. Populate it with default
395 // value "true".
396 vpd::types::PropertyMap l_functionalProp;
397 l_functionalProp.emplace("Functional", true);
398 vpd::vpdSpecificUtility::insertOrMerge(
399 io_interfaces, vpd::constants::operationalStatusInf,
400 move(l_functionalProp));
401 }
402
403 // if chassis is power on. Functional property should be there on D-Bus.
404 // Don't process.
405 return;
406 }
407
processEnabledProperty(const std::string & i_inventoryObjPath,vpd::types::InterfaceMap & io_interfaces) const408 void PrimeInventory::processEnabledProperty(
409 const std::string& i_inventoryObjPath,
410 vpd::types::InterfaceMap& io_interfaces) const noexcept
411 {
412 if (!vpd::dbusUtility::isChassisPowerOn())
413 {
414 std::vector<std::string> l_enableInf{vpd::constants::enableInf};
415
416 auto l_mapperObjectMap =
417 vpd::dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
418
419 // If the object has been found. Check if it is under PIM.
420 if (l_mapperObjectMap.size() != 0)
421 {
422 for (const auto& [l_serviceName, l_interfaceLsit] :
423 l_mapperObjectMap)
424 {
425 if (l_serviceName == vpd::constants::pimServiceName)
426 {
427 // The object is already under PIM. No need to process
428 // again. Retain the old value.
429 return;
430 }
431 }
432 }
433
434 // Implies value is not there in D-Bus. Populate it with default
435 // value "true".
436 vpd::types::PropertyMap l_enabledProp;
437 l_enabledProp.emplace("Enabled", true);
438 vpd::vpdSpecificUtility::insertOrMerge(
439 io_interfaces, vpd::constants::enableInf, move(l_enabledProp));
440 }
441
442 // if chassis is power on. Enabled property should be there on D-Bus.
443 // Don't process.
444 return;
445 }
446