blob: 78d380a49805f016e49807e966c4adcc7d5c0ca7 [file] [log] [blame]
Brandon Wymana0f33ce2019-10-17 18:32:29 -05001#include "psu_manager.hpp"
2
3#include "utility.hpp"
4
Brandon Wymanb76ab242020-09-16 18:06:06 -05005#include <fmt/format.h>
6#include <sys/types.h>
7#include <unistd.h>
8
Brandon Wymanecbecbc2021-08-31 22:53:21 +00009#include <regex>
10
Brandon Wymanaed1f752019-11-25 18:10:52 -060011using namespace phosphor::logging;
12
Brandon Wyman63ea78b2020-09-24 16:49:09 -050013namespace phosphor::power::manager
Brandon Wymana0f33ce2019-10-17 18:32:29 -050014{
15
Brandon Wyman510acaa2020-11-05 18:32:04 -060016constexpr auto IBMCFFPSInterface =
17 "xyz.openbmc_project.Configuration.IBMCFFPSConnector";
18constexpr auto i2cBusProp = "I2CBus";
19constexpr auto i2cAddressProp = "I2CAddress";
20constexpr auto psuNameProp = "Name";
B. J. Wyman681b2a32021-04-20 22:31:22 +000021constexpr auto presLineName = "NamedPresenceGpio";
Brandon Wyman510acaa2020-11-05 18:32:04 -060022
Adriana Kobylak9bab9e12021-02-24 15:32:03 -060023constexpr auto supportedConfIntf =
24 "xyz.openbmc_project.Configuration.SupportedConfiguration";
Adriana Kobylak9bab9e12021-02-24 15:32:03 -060025
Brandon Wyman510acaa2020-11-05 18:32:04 -060026PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e) :
27 bus(bus)
28{
Brandon Wyman510acaa2020-11-05 18:32:04 -060029 // Subscribe to InterfacesAdded before doing a property read, otherwise
30 // the interface could be created after the read attempt but before the
31 // match is created.
32 entityManagerIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
33 bus,
34 sdbusplus::bus::match::rules::interfacesAdded() +
35 sdbusplus::bus::match::rules::sender(
36 "xyz.openbmc_project.EntityManager"),
37 std::bind(&PSUManager::entityManagerIfaceAdded, this,
38 std::placeholders::_1));
39 getPSUConfiguration();
40 getSystemProperties();
41
42 using namespace sdeventplus;
43 auto interval = std::chrono::milliseconds(1000);
44 timer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
45 e, std::bind(&PSUManager::analyze, this), interval);
46
Adriana Kobylaka4d38fa2021-10-05 19:57:47 +000047 validationTimer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
48 e, std::bind(&PSUManager::validateConfig, this));
49
Brandon Wyman510acaa2020-11-05 18:32:04 -060050 // Subscribe to power state changes
51 powerService = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
52 powerOnMatch = std::make_unique<sdbusplus::bus::match_t>(
53 bus,
54 sdbusplus::bus::match::rules::propertiesChanged(POWER_OBJ_PATH,
55 POWER_IFACE),
56 [this](auto& msg) { this->powerStateChanged(msg); });
57
58 initialize();
59}
60
Brandon Wyman510acaa2020-11-05 18:32:04 -060061void PSUManager::getPSUConfiguration()
62{
63 using namespace phosphor::power::util;
64 auto depth = 0;
65 auto objects = getSubTree(bus, "/", IBMCFFPSInterface, depth);
66
67 psus.clear();
68
69 // I should get a map of objects back.
70 // Each object will have a path, a service, and an interface.
71 // The interface should match the one passed into this function.
72 for (const auto& [path, services] : objects)
73 {
74 auto service = services.begin()->first;
75
76 if (path.empty() || service.empty())
77 {
78 continue;
79 }
80
81 // For each object in the array of objects, I want to get properties
82 // from the service, path, and interface.
83 auto properties =
84 getAllProperties(bus, path, IBMCFFPSInterface, service);
85
86 getPSUProperties(properties);
87 }
88
89 if (psus.empty())
90 {
91 // Interface or properties not found. Let the Interfaces Added callback
92 // process the information once the interfaces are added to D-Bus.
93 log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
94 }
95}
96
97void PSUManager::getPSUProperties(util::DbusPropertyMap& properties)
98{
99 // From passed in properties, I want to get: I2CBus, I2CAddress,
100 // and Name. Create a power supply object, using Name to build the inventory
101 // path.
102 const auto basePSUInvPath =
103 "/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply";
104 uint64_t* i2cbus = nullptr;
105 uint64_t* i2caddr = nullptr;
106 std::string* psuname = nullptr;
B. J. Wyman681b2a32021-04-20 22:31:22 +0000107 std::string* preslineptr = nullptr;
Brandon Wyman510acaa2020-11-05 18:32:04 -0600108
109 for (const auto& property : properties)
110 {
111 try
112 {
113 if (property.first == i2cBusProp)
114 {
115 i2cbus = std::get_if<uint64_t>(&properties[i2cBusProp]);
116 }
117 else if (property.first == i2cAddressProp)
118 {
119 i2caddr = std::get_if<uint64_t>(&properties[i2cAddressProp]);
120 }
121 else if (property.first == psuNameProp)
122 {
123 psuname = std::get_if<std::string>(&properties[psuNameProp]);
124 }
B. J. Wyman681b2a32021-04-20 22:31:22 +0000125 else if (property.first == presLineName)
126 {
127 preslineptr =
128 std::get_if<std::string>(&properties[presLineName]);
129 }
Brandon Wyman510acaa2020-11-05 18:32:04 -0600130 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500131 catch (const std::exception& e)
Adriana Kobylak0c9a33d2021-09-13 18:05:09 +0000132 {}
Brandon Wyman510acaa2020-11-05 18:32:04 -0600133 }
134
135 if ((i2cbus) && (i2caddr) && (psuname) && (!psuname->empty()))
136 {
137 std::string invpath = basePSUInvPath;
138 invpath.push_back(psuname->back());
B. J. Wyman681b2a32021-04-20 22:31:22 +0000139 std::string presline = "";
Brandon Wyman510acaa2020-11-05 18:32:04 -0600140
141 log<level::DEBUG>(fmt::format("Inventory Path: {}", invpath).c_str());
142
B. J. Wyman681b2a32021-04-20 22:31:22 +0000143 if (nullptr != preslineptr)
144 {
145 presline = *preslineptr;
146 }
147
Brandon Wymanecbecbc2021-08-31 22:53:21 +0000148 auto invMatch =
149 std::find_if(psus.begin(), psus.end(), [&invpath](auto& psu) {
150 return psu->getInventoryPath() == invpath;
151 });
152 if (invMatch != psus.end())
153 {
154 // This power supply has the same inventory path as the one with
155 // information just added to D-Bus.
156 // Changes to GPIO line name unlikely, so skip checking.
157 // Changes to the I2C bus and address unlikely, as that would
158 // require corresponding device tree updates.
159 // Return out to avoid duplicate object creation.
160 return;
161 }
162
B. J. Wyman681b2a32021-04-20 22:31:22 +0000163 log<level::DEBUG>(
164 fmt::format("make PowerSupply bus: {} addr: {} presline: {}",
165 *i2cbus, *i2caddr, presline)
166 .c_str());
167 auto psu = std::make_unique<PowerSupply>(bus, invpath, *i2cbus,
168 *i2caddr, presline);
Brandon Wyman510acaa2020-11-05 18:32:04 -0600169 psus.emplace_back(std::move(psu));
170 }
171
172 if (psus.empty())
173 {
174 log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
175 }
176}
177
Adriana Kobylake1074d82021-03-16 20:46:44 +0000178void PSUManager::populateSysProperties(const util::DbusPropertyMap& properties)
179{
180 try
181 {
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000182 auto propIt = properties.find("SupportedType");
183 if (propIt == properties.end())
184 {
185 return;
186 }
187 const std::string* type = std::get_if<std::string>(&(propIt->second));
188 if ((type == nullptr) || (*type != "PowerSupply"))
189 {
190 return;
191 }
192
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000193 propIt = properties.find("SupportedModel");
194 if (propIt == properties.end())
195 {
196 return;
197 }
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000198 const std::string* model = std::get_if<std::string>(&(propIt->second));
199 if (model == nullptr)
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000200 {
201 return;
202 }
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000203
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000204 sys_properties sys;
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000205 propIt = properties.find("RedundantCount");
Adriana Kobylake1074d82021-03-16 20:46:44 +0000206 if (propIt != properties.end())
207 {
208 const uint64_t* count = std::get_if<uint64_t>(&(propIt->second));
209 if (count != nullptr)
210 {
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000211 sys.powerSupplyCount = *count;
Adriana Kobylake1074d82021-03-16 20:46:44 +0000212 }
213 }
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000214 propIt = properties.find("InputVoltage");
215 if (propIt != properties.end())
216 {
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000217 const std::vector<uint64_t>* voltage =
218 std::get_if<std::vector<uint64_t>>(&(propIt->second));
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000219 if (voltage != nullptr)
220 {
221 sys.inputVoltage = *voltage;
222 }
223 }
224
Adriana Kobylak886574c2021-11-01 18:22:28 +0000225 // The PowerConfigFullLoad is an optional property, default it to false
226 // since that's the default value of the power-config-full-load GPIO.
227 sys.powerConfigFullLoad = false;
228 propIt = properties.find("PowerConfigFullLoad");
229 if (propIt != properties.end())
230 {
231 const bool* fullLoad = std::get_if<bool>(&(propIt->second));
232 if (fullLoad != nullptr)
233 {
234 sys.powerConfigFullLoad = *fullLoad;
235 }
236 }
237
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000238 supportedConfigs.emplace(*model, sys);
Adriana Kobylake1074d82021-03-16 20:46:44 +0000239 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500240 catch (const std::exception& e)
Adriana Kobylak0c9a33d2021-09-13 18:05:09 +0000241 {}
Adriana Kobylake1074d82021-03-16 20:46:44 +0000242}
243
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600244void PSUManager::getSystemProperties()
245{
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600246
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600247 try
248 {
249 util::DbusSubtree subtree =
250 util::getSubTree(bus, INVENTORY_OBJ_PATH, supportedConfIntf, 0);
Adriana Kobylake1074d82021-03-16 20:46:44 +0000251 if (subtree.empty())
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600252 {
253 throw std::runtime_error("Supported Configuration Not Found");
254 }
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600255
Adriana Kobylake1074d82021-03-16 20:46:44 +0000256 for (const auto& [objPath, services] : subtree)
257 {
258 std::string service = services.begin()->first;
259 if (objPath.empty() || service.empty())
260 {
261 continue;
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600262 }
Adriana Kobylake1074d82021-03-16 20:46:44 +0000263 auto properties = util::getAllProperties(
264 bus, objPath, supportedConfIntf, service);
265 populateSysProperties(properties);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600266 }
267 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500268 catch (const std::exception& e)
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600269 {
270 // Interface or property not found. Let the Interfaces Added callback
271 // process the information once the interfaces are added to D-Bus.
272 }
273}
274
Brandon Wyman3e429132021-03-18 18:03:14 -0500275void PSUManager::entityManagerIfaceAdded(sdbusplus::message::message& msg)
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600276{
277 try
278 {
279 sdbusplus::message::object_path objPath;
Adriana Kobylake1074d82021-03-16 20:46:44 +0000280 std::map<std::string, std::map<std::string, util::DbusVariant>>
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600281 interfaces;
282 msg.read(objPath, interfaces);
283
284 auto itIntf = interfaces.find(supportedConfIntf);
Brandon Wyman510acaa2020-11-05 18:32:04 -0600285 if (itIntf != interfaces.cend())
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600286 {
Brandon Wyman510acaa2020-11-05 18:32:04 -0600287 populateSysProperties(itIntf->second);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600288 }
289
Brandon Wyman510acaa2020-11-05 18:32:04 -0600290 itIntf = interfaces.find(IBMCFFPSInterface);
291 if (itIntf != interfaces.cend())
292 {
293 log<level::INFO>(
294 fmt::format("InterfacesAdded for: {}", IBMCFFPSInterface)
295 .c_str());
296 getPSUProperties(itIntf->second);
297 }
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000298
299 // Call to validate the psu configuration if the power is on and both
300 // the IBMCFFPSConnector and SupportedConfiguration interfaces have been
301 // processed
302 if (powerOn && !psus.empty() && !supportedConfigs.empty())
303 {
Adriana Kobylaka4d38fa2021-10-05 19:57:47 +0000304 validationTimer->restartOnce(validationTimeout);
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000305 }
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600306 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500307 catch (const std::exception& e)
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600308 {
309 // Ignore, the property may be of a different type than expected.
310 }
311}
312
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500313void PSUManager::powerStateChanged(sdbusplus::message::message& msg)
314{
315 int32_t state = 0;
316 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500317 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500318 msg.read(msgSensor, msgData);
319
320 // Check if it was the Present property that changed.
321 auto valPropMap = msgData.find("state");
322 if (valPropMap != msgData.end())
323 {
324 state = std::get<int32_t>(valPropMap->second);
325
326 // Power is on when state=1. Clear faults.
327 if (state)
328 {
329 powerOn = true;
Adriana Kobylaka4d38fa2021-10-05 19:57:47 +0000330 validationTimer->restartOnce(validationTimeout);
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500331 clearFaults();
332 }
333 else
334 {
335 powerOn = false;
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000336 runValidateConfig = true;
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500337 }
338 }
339}
340
Brandon Wyman8b662882021-10-08 17:31:51 +0000341void PSUManager::createError(const std::string& faultName,
342 std::map<std::string, std::string>& additionalData)
Brandon Wymanb76ab242020-09-16 18:06:06 -0500343{
344 using namespace sdbusplus::xyz::openbmc_project;
345 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
346 constexpr auto loggingCreateInterface =
347 "xyz.openbmc_project.Logging.Create";
348
349 try
350 {
Brandon Wyman8b662882021-10-08 17:31:51 +0000351 additionalData["_PID"] = std::to_string(getpid());
352
Brandon Wymanb76ab242020-09-16 18:06:06 -0500353 auto service =
354 util::getService(loggingObjectPath, loggingCreateInterface, bus);
355
356 if (service.empty())
357 {
358 log<level::ERR>("Unable to get logging manager service");
359 return;
360 }
361
362 auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
363 loggingCreateInterface, "Create");
364
365 auto level = Logging::server::Entry::Level::Error;
366 method.append(faultName, level, additionalData);
367
368 auto reply = bus.call(method);
369 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500370 catch (const std::exception& e)
Brandon Wymanb76ab242020-09-16 18:06:06 -0500371 {
372 log<level::ERR>(
373 fmt::format(
374 "Failed creating event log for fault {} due to error {}",
375 faultName, e.what())
376 .c_str());
377 }
378}
379
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500380void PSUManager::analyze()
381{
382 for (auto& psu : psus)
383 {
384 psu->analyze();
385 }
386
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600387 if (powerOn)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500388 {
Adriana Kobylakf2ba1462021-06-24 15:16:17 +0000389 std::map<std::string, std::string> additionalData;
Adriana Kobylakf2ba1462021-06-24 15:16:17 +0000390
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600391 for (auto& psu : psus)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500392 {
Brandon Wymanec0b8dc2021-10-08 21:49:43 +0000393 additionalData.clear();
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600394 // TODO: Fault priorities #918
395 if (!psu->isFaultLogged() && !psu->isPresent())
396 {
Brandon Wymanda369c72021-10-08 18:43:30 +0000397 std::map<std::string, std::string> requiredPSUsData;
398 auto requiredPSUsPresent = hasRequiredPSUs(requiredPSUsData);
Adriana Kobylakf2ba1462021-06-24 15:16:17 +0000399 if (!requiredPSUsPresent)
400 {
Brandon Wymanda369c72021-10-08 18:43:30 +0000401 additionalData.merge(requiredPSUsData);
Adriana Kobylakf2ba1462021-06-24 15:16:17 +0000402 // Create error for power supply missing.
403 additionalData["CALLOUT_INVENTORY_PATH"] =
404 psu->getInventoryPath();
405 additionalData["CALLOUT_PRIORITY"] = "H";
406 createError(
407 "xyz.openbmc_project.Power.PowerSupply.Error.Missing",
408 additionalData);
409 }
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600410 psu->setFaultLogged();
411 }
412 else if (!psu->isFaultLogged() && psu->isFaulted())
413 {
Brandon Wyman786b6f42021-10-12 20:21:41 +0000414 // Add STATUS_WORD and STATUS_MFR last response, in padded
415 // hexadecimal format.
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600416 additionalData["STATUS_WORD"] =
Brandon Wyman786b6f42021-10-12 20:21:41 +0000417 fmt::format("{:#04x}", psu->getStatusWord());
Jay Meyer10d94052020-11-30 14:41:21 -0600418 additionalData["STATUS_MFR"] =
Brandon Wyman786b6f42021-10-12 20:21:41 +0000419 fmt::format("{:#02x}", psu->getMFRFault());
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600420 // If there are faults being reported, they possibly could be
421 // related to a bug in the firmware version running on the power
422 // supply. Capture that data into the error as well.
423 additionalData["FW_VERSION"] = psu->getFWVersion();
424
Brandon Wymanb85b9dd2021-10-19 21:25:17 +0000425 if (psu->hasCommFault())
426 {
Brandon Wyman85c7bf42021-10-19 22:28:48 +0000427 additionalData["STATUS_CML"] =
428 fmt::format("{:#02x}", psu->getStatusCML());
Brandon Wymanb85b9dd2021-10-19 21:25:17 +0000429 /* Attempts to communicate with the power supply have
430 * reached there limit. Create an error. */
431 additionalData["CALLOUT_DEVICE_PATH"] =
432 psu->getDevicePath();
433
434 createError(
435 "xyz.openbmc_project.Power.PowerSupply.Error.CommFault",
436 additionalData);
437
438 psu->setFaultLogged();
439 }
440 else if ((psu->hasInputFault() || psu->hasVINUVFault()))
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600441 {
Brandon Wymanf07bc792021-10-12 19:00:35 +0000442 // Include STATUS_INPUT for input faults.
443 additionalData["STATUS_INPUT"] =
444 fmt::format("{:#02x}", psu->getStatusInput());
445
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600446 /* The power supply location might be needed if the input
447 * fault is due to a problem with the power supply itself.
448 * Include the inventory path with a call out priority of
449 * low.
450 */
451 additionalData["CALLOUT_INVENTORY_PATH"] =
452 psu->getInventoryPath();
453 additionalData["CALLOUT_PRIORITY"] = "L";
454 createError("xyz.openbmc_project.Power.PowerSupply.Error."
455 "InputFault",
456 additionalData);
457 psu->setFaultLogged();
458 }
Brandon Wyman6710ba22021-10-27 17:39:31 +0000459 else if (psu->hasVoutOVFault())
460 {
461 // Include STATUS_VOUT for Vout faults.
462 additionalData["STATUS_VOUT"] =
463 fmt::format("{:#02x}", psu->getStatusVout());
464
465 additionalData["CALLOUT_INVENTORY_PATH"] =
466 psu->getInventoryPath();
467
468 createError(
469 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
470 additionalData);
471
472 psu->setFaultLogged();
473 }
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600474 else if (psu->hasMFRFault())
475 {
476 /* This can represent a variety of faults that result in
477 * calling out the power supply for replacement: Output
478 * OverCurrent, Output Under Voltage, and potentially other
479 * faults.
480 *
481 * Also plan on putting specific fault in AdditionalData,
482 * along with register names and register values
483 * (STATUS_WORD, STATUS_MFR, etc.).*/
484
485 additionalData["CALLOUT_INVENTORY_PATH"] =
486 psu->getInventoryPath();
487
488 createError(
489 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
Brandon Wyman52e54e82020-10-08 14:44:58 -0500490 additionalData);
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500491
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600492 psu->setFaultLogged();
493 }
Brandon Wyman4176d6b2020-10-07 17:41:06 -0500494 }
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500495 }
496 }
497}
498
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000499void PSUManager::validateConfig()
500{
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000501 if (!runValidateConfig || supportedConfigs.empty())
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000502 {
503 return;
504 }
505
Adriana Kobylak4d9aaf92021-06-30 15:27:42 +0000506 std::map<std::string, std::string> additionalData;
507 auto supported = hasRequiredPSUs(additionalData);
508 if (supported)
509 {
510 runValidateConfig = false;
511 return;
512 }
513
514 // Validation failed, create an error log.
515 // Return without setting the runValidateConfig flag to false because
516 // it may be that an additional supported configuration interface is
517 // added and we need to validate it to see if it matches this system.
518 createError("xyz.openbmc_project.Power.PowerSupply.Error.NotSupported",
519 additionalData);
520}
521
522bool PSUManager::hasRequiredPSUs(
523 std::map<std::string, std::string>& additionalData)
524{
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000525 std::string model{};
Adriana Kobylak523704d2021-09-21 15:55:41 +0000526 if (!validateModelName(model, additionalData))
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000527 {
Adriana Kobylak523704d2021-09-21 15:55:41 +0000528 return false;
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000529 }
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000530
Adriana Kobylak4d9aaf92021-06-30 15:27:42 +0000531 auto presentCount =
532 std::count_if(psus.begin(), psus.end(),
533 [](const auto& psu) { return psu->isPresent(); });
534
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000535 // Validate the supported configurations. A system may support more than one
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000536 // power supply model configuration. Since all configurations need to be
537 // checked, the additional data would contain only the information of the
538 // last configuration that did not match.
539 std::map<std::string, std::string> tmpAdditionalData;
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000540 for (const auto& config : supportedConfigs)
541 {
Adriana Kobylak4d9aaf92021-06-30 15:27:42 +0000542 if (config.first != model)
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000543 {
544 continue;
545 }
546 if (presentCount != config.second.powerSupplyCount)
547 {
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000548 tmpAdditionalData.clear();
549 tmpAdditionalData["EXPECTED_COUNT"] =
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000550 std::to_string(config.second.powerSupplyCount);
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000551 tmpAdditionalData["ACTUAL_COUNT"] = std::to_string(presentCount);
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000552 continue;
553 }
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000554
555 bool voltageValidated = true;
556 for (const auto& psu : psus)
557 {
558 if (!psu->isPresent())
559 {
560 // Only present PSUs report a valid input voltage
561 continue;
562 }
563
564 double actualInputVoltage;
565 int inputVoltage;
566 psu->getInputVoltage(actualInputVoltage, inputVoltage);
567
568 if (std::find(config.second.inputVoltage.begin(),
569 config.second.inputVoltage.end(),
570 inputVoltage) == config.second.inputVoltage.end())
571 {
572 tmpAdditionalData.clear();
573 tmpAdditionalData["ACTUAL_VOLTAGE"] =
574 std::to_string(actualInputVoltage);
575 for (const auto& voltage : config.second.inputVoltage)
576 {
577 tmpAdditionalData["EXPECTED_VOLTAGE"] +=
578 std::to_string(voltage) + " ";
579 }
580 tmpAdditionalData["CALLOUT_INVENTORY_PATH"] =
581 psu->getInventoryPath();
582
583 voltageValidated = false;
584 break;
585 }
586 }
587 if (!voltageValidated)
588 {
589 continue;
590 }
591
Adriana Kobylak4d9aaf92021-06-30 15:27:42 +0000592 return true;
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000593 }
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000594
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000595 additionalData.insert(tmpAdditionalData.begin(), tmpAdditionalData.end());
Adriana Kobylak4d9aaf92021-06-30 15:27:42 +0000596 return false;
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000597}
598
Adriana Kobylak523704d2021-09-21 15:55:41 +0000599bool PSUManager::validateModelName(
600 std::string& model, std::map<std::string, std::string>& additionalData)
601{
602 // Check that all PSUs have the same model name. Initialize the model
603 // variable with the first PSU name found, then use it as a base to compare
604 // against the rest of the PSUs.
605 model.clear();
606 for (const auto& psu : psus)
607 {
608 auto psuModel = psu->getModelName();
609 if (psuModel.empty())
610 {
611 continue;
612 }
613 if (model.empty())
614 {
615 model = psuModel;
616 continue;
617 }
618 if (psuModel != model)
619 {
620 additionalData["EXPECTED_MODEL"] = model;
621 additionalData["ACTUAL_MODEL"] = psuModel;
622 additionalData["CALLOUT_INVENTORY_PATH"] = psu->getInventoryPath();
623 model.clear();
624 return false;
625 }
626 }
627 return true;
628}
629
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500630} // namespace phosphor::power::manager