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 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 234 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 uint16_t l_errCode = 0; 306 307 if (auto l_ipzVpdMap = 308 std::get_if<vpd::types::IPZVpdMap>(&i_parsedVpdMap)) 309 { 310 if (!l_record.empty() && !l_keyword.empty() && 311 (*l_ipzVpdMap).count(l_record) && 312 (*l_ipzVpdMap).at(l_record).count(l_keyword)) 313 { 314 auto l_encoded = vpd::vpdSpecificUtility::encodeKeyword( 315 ((*l_ipzVpdMap).at(l_record).at(l_keyword)), 316 l_encoding, l_errCode); 317 318 if (l_errCode) 319 { 320 m_logger->logMessage( 321 "Failed to get encoded keyword value for : " + 322 l_keyword + ", error : " + 323 vpd::commonUtility::getErrCodeMsg(l_errCode)); 324 } 325 326 l_propertyMap.emplace(l_property, l_encoded); 327 } 328 } 329 else if (auto l_kwdVpdMap = 330 std::get_if<vpd::types::KeywordVpdMap>( 331 &i_parsedVpdMap)) 332 { 333 if (!l_keyword.empty() && (*l_kwdVpdMap).count(l_keyword)) 334 { 335 if (auto l_kwValue = 336 std::get_if<vpd::types::BinaryVector>( 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, l_errCode); 344 345 if (l_errCode) 346 { 347 m_logger->logMessage( 348 "Failed to get encoded keyword value for : " + 349 l_keyword + ", error : " + 350 vpd::commonUtility::getErrCodeMsg( 351 l_errCode)); 352 } 353 354 l_propertyMap.emplace(l_property, l_encodedValue); 355 } 356 else if (auto l_kwValue = std::get_if<std::string>( 357 &(*l_kwdVpdMap).at(l_keyword))) 358 { 359 auto l_encodedValue = 360 vpd::vpdSpecificUtility::encodeKeyword( 361 std::string((*l_kwValue).begin(), 362 (*l_kwValue).end()), 363 l_encoding, l_errCode); 364 365 if (l_errCode) 366 { 367 m_logger->logMessage( 368 "Failed to get encoded keyword value for : " + 369 l_keyword + ", error : " + 370 vpd::commonUtility::getErrCodeMsg( 371 l_errCode)); 372 } 373 374 l_propertyMap.emplace(l_property, l_encodedValue); 375 } 376 else if (auto l_uintValue = std::get_if<size_t>( 377 &(*l_kwdVpdMap).at(l_keyword))) 378 { 379 l_propertyMap.emplace(l_property, *l_uintValue); 380 } 381 else 382 { 383 m_logger->logMessage( 384 "Unknown keyword found, Keywrod = " + 385 l_keyword); 386 } 387 } 388 } 389 } 390 } 391 vpd::vpdSpecificUtility::insertOrMerge(io_interfaceMap, l_interface, 392 move(l_propertyMap)); 393 } 394 } 395 396 void PrimeInventory::processFunctionalProperty( 397 const std::string& i_inventoryObjPath, 398 vpd::types::InterfaceMap& io_interfaces) const noexcept 399 { 400 if (!vpd::dbusUtility::isChassisPowerOn()) 401 { 402 std::vector<std::string> l_operationalStatusInf{ 403 vpd::constants::operationalStatusInf}; 404 405 auto l_mapperObjectMap = vpd::dbusUtility::getObjectMap( 406 i_inventoryObjPath, l_operationalStatusInf); 407 408 // If the object has been found. Check if it is under PIM. 409 if (l_mapperObjectMap.size() != 0) 410 { 411 for (const auto& [l_serviceName, l_interfaceLsit] : 412 l_mapperObjectMap) 413 { 414 if (l_serviceName == vpd::constants::pimServiceName) 415 { 416 // The object is already under PIM. No need to process 417 // again. Retain the old value. 418 return; 419 } 420 } 421 } 422 423 // Implies value is not there in D-Bus. Populate it with default 424 // value "true". 425 vpd::types::PropertyMap l_functionalProp; 426 l_functionalProp.emplace("Functional", true); 427 vpd::vpdSpecificUtility::insertOrMerge( 428 io_interfaces, vpd::constants::operationalStatusInf, 429 move(l_functionalProp)); 430 } 431 432 // if chassis is power on. Functional property should be there on D-Bus. 433 // Don't process. 434 return; 435 } 436 437 void PrimeInventory::processEnabledProperty( 438 const std::string& i_inventoryObjPath, 439 vpd::types::InterfaceMap& io_interfaces) const noexcept 440 { 441 if (!vpd::dbusUtility::isChassisPowerOn()) 442 { 443 std::vector<std::string> l_enableInf{vpd::constants::enableInf}; 444 445 auto l_mapperObjectMap = 446 vpd::dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf); 447 448 // If the object has been found. Check if it is under PIM. 449 if (l_mapperObjectMap.size() != 0) 450 { 451 for (const auto& [l_serviceName, l_interfaceLsit] : 452 l_mapperObjectMap) 453 { 454 if (l_serviceName == vpd::constants::pimServiceName) 455 { 456 // The object is already under PIM. No need to process 457 // again. Retain the old value. 458 return; 459 } 460 } 461 } 462 463 // Implies value is not there in D-Bus. Populate it with default 464 // value "true". 465 vpd::types::PropertyMap l_enabledProp; 466 l_enabledProp.emplace("Enabled", true); 467 vpd::vpdSpecificUtility::insertOrMerge( 468 io_interfaces, vpd::constants::enableInf, move(l_enabledProp)); 469 } 470 471 // if chassis is power on. Enabled property should be there on D-Bus. 472 // Don't process. 473 return; 474 } 475