blob: 665a7afe414b61c3257b1db15363239009382f84 [file] [log] [blame]
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +05301#include "config.h"
2
Sunny Srivastava867ee752025-04-15 12:24:23 +05303#include "ibm_handler.hpp"
4
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +05305#include "parser.hpp"
6
7#include <utility/dbus_utility.hpp>
8#include <utility/json_utility.hpp>
9#include <utility/vpd_specific_utility.hpp>
10
Sunny Srivastava867ee752025-04-15 12:24:23 +053011namespace vpd
12{
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053013IbmHandler::IbmHandler(
14 std::shared_ptr<Worker>& o_worker,
15 std::shared_ptr<BackupAndRestore>& o_backupAndRestoreObj,
16 const std::shared_ptr<sdbusplus::asio::dbus_interface>& i_iFace,
17 const std::shared_ptr<boost::asio::io_context>& i_ioCon,
18 const std::shared_ptr<sdbusplus::asio::connection>& i_asioConnection) :
19 m_worker(o_worker), m_backupAndRestoreObj(o_backupAndRestoreObj),
20 m_interface(i_iFace), m_ioContext(i_ioCon),
21 m_asioConnection(i_asioConnection)
22{
23 if (dbusUtility::isChassisPowerOn())
24 {
25 // At power on, less number of FRU(s) needs collection. we can scale
26 // down the threads to reduce CPU utilization.
27 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT,
28 constants::VALUE_1);
29 }
30 else
31 {
32 // Initialize with default configuration
33 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT);
34 }
35
36 // Set up minimal things that is needed before bus name is claimed.
37 m_worker->performInitialSetup();
38
39 // Get the system config JSON object
40 m_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
41
42 if (!m_sysCfgJsonObj.empty() &&
43 jsonUtility::isBackupAndRestoreRequired(m_sysCfgJsonObj))
44 {
45 try
46 {
47 m_backupAndRestoreObj =
48 std::make_shared<BackupAndRestore>(m_sysCfgJsonObj);
49 }
50 catch (const std::exception& l_ex)
51 {
52 logging::logMessage("Back up and restore instantiation failed. {" +
53 std::string(l_ex.what()) + "}");
54
55 EventLogger::createSyncPel(
56 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
57 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
58 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
59 }
60 }
61
62 // set callback to detect any asset tag change
63 registerAssetTagChangeCallback();
64
65 // set async timer to detect if system VPD is published on D-Bus.
66 SetTimerToDetectSVPDOnDbus();
67
68 // set async timer to detect if VPD collection is done.
69 SetTimerToDetectVpdCollectionStatus();
70
71 // Instantiate GpioMonitor class
72 m_gpioMonitor =
73 std::make_shared<GpioMonitor>(m_sysCfgJsonObj, m_worker, m_ioContext);
74}
75
76void IbmHandler::registerAssetTagChangeCallback()
77{
78 static std::shared_ptr<sdbusplus::bus::match_t> l_assetMatch =
79 std::make_shared<sdbusplus::bus::match_t>(
80 *m_asioConnection,
81 sdbusplus::bus::match::rules::propertiesChanged(
82 constants::systemInvPath, constants::assetTagInf),
83 [this](sdbusplus::message_t& l_msg) {
84 processAssetTagChangeCallback(l_msg);
85 });
86}
87
88void IbmHandler::processAssetTagChangeCallback(sdbusplus::message_t& i_msg)
89{
90 try
91 {
92 if (i_msg.is_method_error())
93 {
94 throw std::runtime_error(
95 "Error reading callback msg for asset tag.");
96 }
97
98 std::string l_objectPath;
99 types::PropertyMap l_propMap;
100 i_msg.read(l_objectPath, l_propMap);
101
102 const auto& l_itrToAssetTag = l_propMap.find("AssetTag");
103 if (l_itrToAssetTag != l_propMap.end())
104 {
105 if (auto l_assetTag =
106 std::get_if<std::string>(&(l_itrToAssetTag->second)))
107 {
108 // Call Notify to persist the AssetTag
109 types::ObjectMap l_objectMap = {
110 {sdbusplus::message::object_path(constants::systemInvPath),
111 {{constants::assetTagInf, {{"AssetTag", *l_assetTag}}}}}};
112
113 // Notify PIM
114 if (!dbusUtility::callPIM(move(l_objectMap)))
115 {
116 throw std::runtime_error(
117 "Call to PIM failed for asset tag update.");
118 }
119 }
120 }
121 else
122 {
123 throw std::runtime_error(
124 "Could not find asset tag in callback message.");
125 }
126 }
127 catch (const std::exception& l_ex)
128 {
129 // TODO: Log PEL with below description.
130 logging::logMessage("Asset tag callback update failed with error: " +
131 std::string(l_ex.what()));
132 }
133}
134
135void IbmHandler::SetTimerToDetectSVPDOnDbus()
136{
137 try
138 {
139 static boost::asio::steady_timer timer(*m_ioContext);
140
141 // timer for 2 seconds
142 auto asyncCancelled = timer.expires_after(std::chrono::seconds(2));
143
144 (asyncCancelled == 0) ? logging::logMessage("Timer started")
145 : logging::logMessage("Timer re-started");
146
147 timer.async_wait([this](const boost::system::error_code& ec) {
148 if (ec == boost::asio::error::operation_aborted)
149 {
150 throw std::runtime_error(
151 std::string(__FUNCTION__) +
152 ": Timer to detect system VPD collection status was aborted.");
153 }
154
155 if (ec)
156 {
157 throw std::runtime_error(
158 std::string(__FUNCTION__) +
159 ": Timer to detect System VPD collection failed");
160 }
161
162 if (m_worker->isSystemVPDOnDBus())
163 {
164 // cancel the timer
165 timer.cancel();
166
167 // Triggering FRU VPD collection. Setting status to "In
168 // Progress".
169 m_interface->set_property("CollectionStatus",
170 std::string("InProgress"));
171 m_worker->collectFrusFromJson();
172 }
173 });
174 }
175 catch (const std::exception& l_ex)
176 {
177 EventLogger::createAsyncPel(
178 EventLogger::getErrorType(l_ex), types::SeverityType::Critical,
179 __FILE__, __FUNCTION__, 0,
180 std::string("Collection for FRUs failed with reason:") +
181 EventLogger::getErrorMsg(l_ex),
182 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
183 }
184}
185
186void IbmHandler::SetTimerToDetectVpdCollectionStatus()
187{
188 // Keeping max retry for 2 minutes. TODO: Make it configurable based on
189 // system type.
190 static constexpr auto MAX_RETRY = 12;
191
192 static boost::asio::steady_timer l_timer(*m_ioContext);
193 static uint8_t l_timerRetry = 0;
194
195 auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(10));
196
197 (l_asyncCancelled == 0)
198 ? logging::logMessage("Collection Timer started")
199 : logging::logMessage("Collection Timer re-started");
200
201 l_timer.async_wait([this](const boost::system::error_code& ec) {
202 if (ec == boost::asio::error::operation_aborted)
203 {
204 throw std::runtime_error(
205 "Timer to detect thread collection status was aborted");
206 }
207
208 if (ec)
209 {
210 throw std::runtime_error(
211 "Timer to detect thread collection failed");
212 }
213
214 if (m_worker->isAllFruCollectionDone())
215 {
216 // cancel the timer
217 l_timer.cancel();
218 processFailedEeproms();
219
220 // update VPD for powerVS system.
221 ConfigurePowerVsSystem();
222
223 std::cout << "m_worker->isSystemVPDOnDBus() completed" << std::endl;
224 m_interface->set_property("CollectionStatus",
225 std::string("Completed"));
226
227 if (m_backupAndRestoreObj)
228 {
229 m_backupAndRestoreObj->backupAndRestore();
230 }
231 }
232 else
233 {
234 auto l_threadCount = m_worker->getActiveThreadCount();
235 if (l_timerRetry == MAX_RETRY)
236 {
237 l_timer.cancel();
238 logging::logMessage("Taking too long. Active thread = " +
239 std::to_string(l_threadCount));
240 }
241 else
242 {
243 l_timerRetry++;
244 logging::logMessage("Collection is in progress for [" +
245 std::to_string(l_threadCount) + "] FRUs.");
246
247 SetTimerToDetectVpdCollectionStatus();
248 }
249 }
250 });
251}
252
253void IbmHandler::checkAndUpdatePowerVsVpd(
254 const nlohmann::json& i_powerVsJsonObj,
255 std::vector<std::string>& o_failedPathList)
256{
257 for (const auto& [l_fruPath, l_recJson] : i_powerVsJsonObj.items())
258 {
259 nlohmann::json l_sysCfgJsonObj{};
260 if (m_worker.get() != nullptr)
261 {
262 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
263 }
264
265 // The utility method will handle emty JSON case. No explicit
266 // handling required here.
267 auto l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
268 l_sysCfgJsonObj, l_fruPath);
269
270 // Mark it as failed if inventory path not found in JSON.
271 if (l_inventoryPath.empty())
272 {
273 o_failedPathList.push_back(l_fruPath);
274 continue;
275 }
276
277 // check if the FRU is present
278 if (!dbusUtility::isInventoryPresent(l_inventoryPath))
279 {
280 logging::logMessage(
281 "Inventory not present, skip updating part number. Path: " +
282 l_inventoryPath);
283 continue;
284 }
285
286 // check if the FRU needs CCIN check before updating PN.
287 if (l_recJson.contains("CCIN"))
288 {
289 const auto& l_ccinFromDbus =
290 vpdSpecificUtility::getCcinFromDbus(l_inventoryPath);
291
292 // Not an ideal situation as CCIN can't be empty.
293 if (l_ccinFromDbus.empty())
294 {
295 o_failedPathList.push_back(l_fruPath);
296 continue;
297 }
298
299 std::vector<std::string> l_ccinListFromJson = l_recJson["CCIN"];
300
301 if (find(l_ccinListFromJson.begin(), l_ccinListFromJson.end(),
302 l_ccinFromDbus) == l_ccinListFromJson.end())
303 {
304 // Don't update PN in this case.
305 continue;
306 }
307 }
308
309 for (const auto& [l_recordName, l_kwdJson] : l_recJson.items())
310 {
311 // Record name can't be CCIN, skip processing as it is there for PN
312 // update based on CCIN check.
313 if (l_recordName == constants::kwdCCIN)
314 {
315 continue;
316 }
317
318 for (const auto& [l_kwdName, l_kwdValue] : l_kwdJson.items())
319 {
320 // Is value of type array.
321 if (!l_kwdValue.is_array())
322 {
323 o_failedPathList.push_back(l_fruPath);
324 continue;
325 }
326
327 // Get current FRU Part number.
328 auto l_retVal = dbusUtility::readDbusProperty(
329 constants::pimServiceName, l_inventoryPath,
330 constants::viniInf, constants::kwdFN);
331
332 auto l_ptrToFn = std::get_if<types::BinaryVector>(&l_retVal);
333
334 if (!l_ptrToFn)
335 {
336 o_failedPathList.push_back(l_fruPath);
337 continue;
338 }
339
340 types::BinaryVector l_binaryKwdValue =
341 l_kwdValue.get<types::BinaryVector>();
342 if (l_binaryKwdValue == (*l_ptrToFn))
343 {
344 continue;
345 }
346
347 // Update part number only if required.
348 std::shared_ptr<Parser> l_parserObj =
349 std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
350 if (l_parserObj->updateVpdKeyword(std::make_tuple(
351 l_recordName, l_kwdName, l_binaryKwdValue)) ==
352 constants::FAILURE)
353 {
354 o_failedPathList.push_back(l_fruPath);
355 continue;
356 }
357
358 // update the Asset interface Spare part number explicitly.
359 if (!dbusUtility::callPIM(types::ObjectMap{
360 {l_inventoryPath,
361 {{constants::assetInf,
362 {{"SparePartNumber",
363 std::string(l_binaryKwdValue.begin(),
364 l_binaryKwdValue.end())}}}}}}))
365 {
366 logging::logMessage(
367 "Updating Spare Part Number under Asset interface failed for path [" +
368 l_inventoryPath + "]");
369 }
370
371 // Just needed for logging.
372 std::string l_initialPartNum((*l_ptrToFn).begin(),
373 (*l_ptrToFn).end());
374 std::string l_finalPartNum(l_binaryKwdValue.begin(),
375 l_binaryKwdValue.end());
376 logging::logMessage(
377 "FRU Part number updated for path [" + l_inventoryPath +
378 "]" + "From [" + l_initialPartNum + "]" + " to [" +
379 l_finalPartNum + "]");
380 }
381 }
382 }
383}
384
385void IbmHandler::ConfigurePowerVsSystem()
386{
387 std::vector<std::string> l_failedPathList;
388 try
389 {
390 types::BinaryVector l_imValue = dbusUtility::getImFromDbus();
391 if (l_imValue.empty())
392 {
393 throw DbusException("Invalid IM value read from Dbus");
394 }
395
396 if (!vpdSpecificUtility::isPowerVsConfiguration(l_imValue))
397 {
398 // TODO: Should booting be blocked in case of some
399 // misconfigurations?
400 return;
401 }
402
403 const nlohmann::json& l_powerVsJsonObj =
404 jsonUtility::getPowerVsJson(l_imValue);
405
406 if (l_powerVsJsonObj.empty())
407 {
408 throw std::runtime_error("PowerVS Json not found");
409 }
410
411 checkAndUpdatePowerVsVpd(l_powerVsJsonObj, l_failedPathList);
412
413 if (!l_failedPathList.empty())
414 {
415 throw std::runtime_error(
416 "Part number update failed for following paths: ");
417 }
418 }
419 catch (const std::exception& l_ex)
420 {
421 // TODO log appropriate PEL
422 }
423}
424
425void IbmHandler::processFailedEeproms()
426{
427 if (m_worker.get() != nullptr)
428 {
429 // TODO:
430 // - iterate through list of EEPROMs for which thread creation has
431 // failed
432 // - For each failed EEPROM, trigger VPD collection
433 m_worker->getFailedEepromPaths().clear();
434 }
435}
Sunny Srivastava867ee752025-04-15 12:24:23 +0530436} // namespace vpd