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 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 51 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 92 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 147 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 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 241 return true; 242 } 243 244 void PrimeInventory::populateInterfaces( 245 const nlohmann::json& i_interfaceJson, 246 vpd::types::InterfaceMap& io_interfaceMap, 247 const vpd::types::VPDMapVariant& i_parsedVpdMap) const noexcept 248 { 249 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 315 uint16_t l_errCode = 0; 316 317 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)), 326 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 336 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()), 353 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 } 363 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()), 373 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 } 383 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 } 404 } 405 406 void PrimeInventory::processFunctionalProperty( 407 const std::string& i_inventoryObjPath, 408 vpd::types::InterfaceMap& io_interfaces) const noexcept 409 { 410 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; 445 } 446 447 void PrimeInventory::processEnabledProperty( 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_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; 484 } 485