blob: cff542f598c49e1346d1796eacd70c3a204ef64c [file] [log] [blame]
Cheng C Yang209ec562019-03-12 16:37:44 +08001/*
2// Copyright (c) 2019 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
Cheng C Yang58b2b532019-05-31 00:19:45 +080017#include <PSUEvent.hpp>
Cheng C Yang209ec562019-03-12 16:37:44 +080018#include <PSUSensor.hpp>
19#include <Utils.hpp>
20#include <boost/algorithm/string/predicate.hpp>
21#include <boost/algorithm/string/replace.hpp>
22#include <boost/container/flat_set.hpp>
James Feist24f02f22019-04-15 11:05:39 -070023#include <filesystem>
Cheng C Yang209ec562019-03-12 16:37:44 +080024#include <fstream>
Cheng C Yang58b2b532019-05-31 00:19:45 +080025#include <iostream>
Cheng C Yang209ec562019-03-12 16:37:44 +080026#include <regex>
27#include <sdbusplus/asio/connection.hpp>
28#include <sdbusplus/asio/object_server.hpp>
29
30static constexpr std::array<const char*, 1> sensorTypes = {
31 "xyz.openbmc_project.Configuration.pmbus"};
32
Vijay Khemka996bad12019-05-28 15:15:16 -070033static std::vector<std::string> pmbusNames = {"pmbus", "pxe1610"};
Cheng C Yang209ec562019-03-12 16:37:44 +080034namespace fs = std::filesystem;
35
Cheng C Yang916360b2019-05-07 18:47:16 +080036static boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>>
37 sensors;
Cheng C Yang58b2b532019-05-31 00:19:45 +080038static boost::container::flat_map<std::string, std::unique_ptr<PSUCombineEvent>>
39 combineEvents;
Cheng C Yang916360b2019-05-07 18:47:16 +080040static boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
41 pwmSensors;
42static boost::container::flat_map<std::string, std::string> sensorTable;
43static boost::container::flat_map<std::string, PSUProperty> labelMatch;
44static boost::container::flat_map<std::string, std::string> pwmTable;
Cheng C Yang58b2b532019-05-31 00:19:45 +080045static boost::container::flat_map<std::string, std::vector<std::string>>
46 eventMatch;
47static boost::container::flat_map<std::string, std::vector<std::string>>
48 limitEventMatch;
49
50// Function CheckEvent will check each attribute from eventMatch table in the
51// sysfs. If the attributes exists in sysfs, then store the complete path
52// of the attribute into eventPathList.
53void checkEvent(
54 const std::string& directory,
55 const boost::container::flat_map<std::string, std::vector<std::string>>&
56 eventMatch,
57 boost::container::flat_map<std::string, std::vector<std::string>>&
58 eventPathList)
59{
60 for (const auto& match : eventMatch)
61 {
62 const std::vector<std::string>& eventAttrs = match.second;
63 const std::string& eventName = match.first;
64 for (const auto& eventAttr : eventAttrs)
65 {
66 auto eventPath = directory + "/" + eventAttr;
67
68 std::ifstream eventFile(eventPath);
69 if (!eventFile.good())
70 {
71 continue;
72 }
73
74 eventPathList[eventName].push_back(eventPath);
75 }
76 }
77}
78
79// Function checkEventLimits will check all the psu related xxx_input attributes
80// in sysfs to see if xxx_crit_alarm xxx_lcrit_alarm xxx_max_alarm
81// xxx_min_alarm exist, then store the existing paths of the alarm attributes
82// to eventPathList.
83void checkEventLimits(
84 const std::string& sensorPathStr,
85 const boost::container::flat_map<std::string, std::vector<std::string>>&
86 limitEventMatch,
87 boost::container::flat_map<std::string, std::vector<std::string>>&
88 eventPathList)
89{
90 for (const auto& limitMatch : limitEventMatch)
91 {
92 const std::vector<std::string>& limitEventAttrs = limitMatch.second;
93 const std::string& eventName = limitMatch.first;
94 for (const auto& limitEventAttr : limitEventAttrs)
95 {
96 auto limitEventPath =
97 boost::replace_all_copy(sensorPathStr, "input", limitEventAttr);
98 std::ifstream eventFile(limitEventPath);
99 if (!eventFile.good())
100 {
101 continue;
102 }
103 eventPathList[eventName].push_back(limitEventPath);
104 }
105 }
106}
Cheng C Yang916360b2019-05-07 18:47:16 +0800107
108static void checkPWMSensor(const fs::path& sensorPath, std::string& labelHead,
109 const std::string& interfacePath,
110 sdbusplus::asio::object_server& objectServer,
111 std::string psuName)
112{
113 for (const auto& pwmName : pwmTable)
114 {
115 if (pwmName.first != labelHead)
116 {
117 continue;
118 }
119
120 const std::string& sensorPathStr = sensorPath.string();
121 const std::string& pwmPathStr =
122 boost::replace_all_copy(sensorPathStr, "input", "target");
123 std::ifstream pwmFile(pwmPathStr);
124 if (!pwmFile.good())
125 {
126 continue;
127 }
128
129 auto findPWMSensor = pwmSensors.find(psuName + labelHead);
130 if (findPWMSensor != pwmSensors.end())
131 {
132 continue;
133 }
134
135 pwmSensors[psuName + labelHead] = std::make_unique<PwmSensor>(
136 "Pwm_" + psuName + "_" + pwmName.second, pwmPathStr, objectServer,
Cheng C Yang15266a92019-06-12 08:42:52 +0800137 interfacePath + "_" + pwmName.second);
Cheng C Yang916360b2019-05-07 18:47:16 +0800138 }
139}
140
141void createSensors(boost::asio::io_service& io,
142 sdbusplus::asio::object_server& objectServer,
143 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
Cheng C Yang209ec562019-03-12 16:37:44 +0800144{
145
146 ManagedObjectType sensorConfigs;
147 bool useCache = false;
148
Cheng C Yang58b2b532019-05-31 00:19:45 +0800149 // TODO may need only modify the ones that need to be changed.
150 sensors.clear();
Cheng C Yang209ec562019-03-12 16:37:44 +0800151 for (const char* type : sensorTypes)
152 {
153 if (!getSensorConfiguration(type, dbusConnection, sensorConfigs,
154 useCache))
155 {
156 std::cerr << "error get sensor config from entity manager\n";
157 return;
158 }
159 useCache = true;
160 }
161
162 std::vector<fs::path> pmbusPaths;
163 if (!findFiles(fs::path("/sys/class/hwmon"), "name", pmbusPaths))
164 {
165 std::cerr << "No PSU sensors in system\n";
166 return;
167 }
168
169 boost::container::flat_set<std::string> directories;
170 for (const auto& pmbusPath : pmbusPaths)
171 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800172 boost::container::flat_map<std::string, std::vector<std::string>>
173 eventPathList;
174
175 std::ifstream nameFile(pmbusPath);
176 if (!nameFile.good())
177 {
178 std::cerr << "Failure reading " << pmbusPath << "\n";
179 continue;
180 }
181
182 std::string pmbusName;
183 std::getline(nameFile, pmbusName);
184 nameFile.close();
Vijay Khemka996bad12019-05-28 15:15:16 -0700185
186 if (std::find(pmbusNames.begin(), pmbusNames.end(), pmbusName) ==
187 pmbusNames.end())
Cheng C Yang58b2b532019-05-31 00:19:45 +0800188 {
189 continue;
190 }
191
192 const std::string* psuName;
Cheng C Yang209ec562019-03-12 16:37:44 +0800193 auto directory = pmbusPath.parent_path();
194
195 auto ret = directories.insert(directory.string());
196 if (!ret.second)
197 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800198 continue; // check if path has already been searched
Cheng C Yang209ec562019-03-12 16:37:44 +0800199 }
200
201 auto device = fs::path(directory / "device");
202 std::string deviceName = fs::canonical(device).stem();
203 auto findHyphen = deviceName.find("-");
204 if (findHyphen == std::string::npos)
205 {
206 std::cerr << "found bad device" << deviceName << "\n";
207 continue;
208 }
209 std::string busStr = deviceName.substr(0, findHyphen);
210 std::string addrStr = deviceName.substr(findHyphen + 1);
211
212 size_t bus = 0;
213 size_t addr = 0;
214
215 try
216 {
217 bus = std::stoi(busStr);
218 addr = std::stoi(addrStr, 0, 16);
219 }
220 catch (std::invalid_argument)
221 {
222 continue;
223 }
224
Cheng C Yang209ec562019-03-12 16:37:44 +0800225 const std::pair<std::string, boost::container::flat_map<
226 std::string, BasicVariantType>>*
227 baseConfig = nullptr;
228 const SensorData* sensorData = nullptr;
229 const std::string* interfacePath = nullptr;
230 const char* sensorType = nullptr;
231
232 for (const std::pair<sdbusplus::message::object_path, SensorData>&
233 sensor : sensorConfigs)
234 {
235 sensorData = &(sensor.second);
236 for (const char* type : sensorTypes)
237 {
238 auto sensorBase = sensorData->find(type);
239 if (sensorBase != sensorData->end())
240 {
241 baseConfig = &(*sensorBase);
242 sensorType = type;
243 break;
244 }
245 }
246 if (baseConfig == nullptr)
247 {
248 std::cerr << "error finding base configuration for "
249 << deviceName << "\n";
250 continue;
251 }
252
253 auto configBus = baseConfig->second.find("Bus");
254 auto configAddress = baseConfig->second.find("Address");
255
256 if (configBus == baseConfig->second.end() ||
257 configAddress == baseConfig->second.end())
258 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800259 std::cerr << "error finding necessary entry in configuration\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800260 continue;
261 }
262
Cheng C Yang58b2b532019-05-31 00:19:45 +0800263 const uint64_t* confBus;
264 const uint64_t* confAddr;
265 if (!(confBus = std::get_if<uint64_t>(&(configBus->second))) ||
266 !(confAddr = std::get_if<uint64_t>(&(configAddress->second))))
267 {
268 std::cerr
269 << "Canot get bus or address, invalid configuration\n";
270 continue;
271 }
272
273 if ((*confBus != bus) || (*confAddr != addr))
Cheng C Yang209ec562019-03-12 16:37:44 +0800274 {
275 continue;
276 }
277
278 interfacePath = &(sensor.first.str);
279 break;
280 }
281 if (interfacePath == nullptr)
282 {
283 std::cerr << "failed to find match for " << deviceName << "\n";
284 continue;
285 }
286
Cheng C Yange50345b2019-04-02 17:26:15 +0800287 auto findPSUName = baseConfig->second.find("Name");
288 if (findPSUName == baseConfig->second.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800289 {
290 std::cerr << "could not determine configuration name for "
291 << deviceName << "\n";
292 continue;
293 }
294
Cheng C Yang58b2b532019-05-31 00:19:45 +0800295 if (!(psuName = std::get_if<std::string>(&(findPSUName->second))))
296 {
297 std::cerr << "Cannot find psu name, invalid configuration\n";
298 continue;
299 }
300 checkEvent(directory.string(), eventMatch, eventPathList);
301
Vijay Khemka996bad12019-05-28 15:15:16 -0700302 /* Check if there are more sensors in the same interface */
303 int i = 1;
304 std::vector<std::string> psuNames;
305 do
306 {
307 psuNames.push_back(std::get<std::string>(findPSUName->second));
308 findPSUName = baseConfig->second.find("Name" + std::to_string(i++));
309 } while (findPSUName != baseConfig->second.end());
310
Cheng C Yange50345b2019-04-02 17:26:15 +0800311 std::vector<fs::path> sensorPaths;
312 if (!findFiles(fs::path(directory), R"(\w\d+_input$)", sensorPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800313 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800314 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800315 continue;
316 }
317
Vijay Khemka996bad12019-05-28 15:15:16 -0700318 /* Find array of labels to be exposed if it is defined in config */
319 std::vector<std::string> findLabels;
320 auto findLabelObj = baseConfig->second.find("Labels");
321 if (findLabelObj != baseConfig->second.end())
322 {
323 findLabels =
324 std::get<std::vector<std::string>>(findLabelObj->second);
325 }
326
Cheng C Yange50345b2019-04-02 17:26:15 +0800327 for (const auto& sensorPath : sensorPaths)
Cheng C Yang209ec562019-03-12 16:37:44 +0800328 {
Cheng C Yang209ec562019-03-12 16:37:44 +0800329
Cheng C Yange50345b2019-04-02 17:26:15 +0800330 std::string labelHead;
331 std::string sensorPathStr = sensorPath.string();
332 std::string sensorNameStr = sensorPath.filename();
333 std::string sensorNameSubStr =
334 sensorNameStr.substr(0, sensorNameStr.find("_") - 1);
335
336 std::string labelPathStr =
337 boost::replace_all_copy(sensorNameStr, "input", "label");
338 std::vector<fs::path> labelPaths;
339 if (!findFiles(fs::path(directory), labelPathStr, labelPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800340 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800341 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800342 continue;
343 }
344
Cheng C Yange50345b2019-04-02 17:26:15 +0800345 if (labelPaths.empty())
Cheng C Yang209ec562019-03-12 16:37:44 +0800346 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800347 labelHead = sensorNameStr.substr(0, sensorNameStr.find("_"));
Cheng C Yang209ec562019-03-12 16:37:44 +0800348 }
349 else
350 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800351 auto labelPath =
352 boost::replace_all_copy(sensorPathStr, "input", "label");
353 std::ifstream labelFile(labelPath);
354 if (!labelFile.good())
355 {
356 std::cerr << "Failure reading " << sensorPath << "\n";
357 continue;
358 }
359 std::string label;
360 std::getline(labelFile, label);
361 labelFile.close();
Cheng C Yang209ec562019-03-12 16:37:44 +0800362
Cheng C Yange50345b2019-04-02 17:26:15 +0800363 auto findSensor = sensors.find(label);
364 if (findSensor != sensors.end())
365 {
366 continue;
367 }
368
369 labelHead = label.substr(0, label.find(" "));
370 }
371
Cheng C Yang916360b2019-05-07 18:47:16 +0800372 checkPWMSensor(sensorPath, labelHead, *interfacePath, objectServer,
Vijay Khemka996bad12019-05-28 15:15:16 -0700373 psuNames[0]);
Cheng C Yang916360b2019-05-07 18:47:16 +0800374
Vijay Khemka996bad12019-05-28 15:15:16 -0700375 if (!findLabels.empty())
376 {
377 /* Check if this labelHead is enabled in config file */
378 if (std::find(findLabels.begin(), findLabels.end(),
379 labelHead) == findLabels.end())
380 {
381 continue;
382 }
383 }
Cheng C Yange50345b2019-04-02 17:26:15 +0800384
Vijay Khemka996bad12019-05-28 15:15:16 -0700385 /* Find out sensor name index for this label */
386 uint8_t nameIndex = labelHead[labelHead.size() - 1];
387 if (nameIndex > '1' && nameIndex <= '9')
388 {
389 nameIndex -= '1';
390 if (psuNames.size() <= nameIndex)
391 {
392 continue;
393 }
394 }
395 else
396 nameIndex = 0;
Cheng C Yange50345b2019-04-02 17:26:15 +0800397
398 auto findProperty = labelMatch.find(labelHead);
399 if (findProperty == labelMatch.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800400 {
Cheng C Yang209ec562019-03-12 16:37:44 +0800401 continue;
402 }
403
Cheng C Yang58b2b532019-05-31 00:19:45 +0800404 checkEventLimits(sensorPathStr, limitEventMatch, eventPathList);
405
Cheng C Yange50345b2019-04-02 17:26:15 +0800406 unsigned int factor =
407 std::pow(10, findProperty->second.sensorScaleFactor);
Vijay Khemka996bad12019-05-28 15:15:16 -0700408
409 std::vector<thresholds::Threshold> sensorThresholds;
410
411 parseThresholdsFromConfig(*sensorData, sensorThresholds,
412 &labelHead);
Cheng C Yange50345b2019-04-02 17:26:15 +0800413 if (sensorThresholds.empty())
414 {
Vijay Khemka996bad12019-05-28 15:15:16 -0700415 continue;
Cheng C Yange50345b2019-04-02 17:26:15 +0800416 }
417
418 auto findSensorType = sensorTable.find(sensorNameSubStr);
419 if (findSensorType == sensorTable.end())
420 {
421 std::cerr << "Cannot find PSU sensorType\n";
422 continue;
423 }
424
425 std::string sensorName =
Vijay Khemka996bad12019-05-28 15:15:16 -0700426 psuNames[nameIndex] + " " + findProperty->second.labelTypeName;
Cheng C Yange50345b2019-04-02 17:26:15 +0800427
Cheng C Yang58b2b532019-05-31 00:19:45 +0800428 sensors[sensorName] = std::make_unique<PSUSensor>(
Cheng C Yange50345b2019-04-02 17:26:15 +0800429 sensorPathStr, sensorType, objectServer, dbusConnection, io,
Cheng C Yang209ec562019-03-12 16:37:44 +0800430 sensorName, std::move(sensorThresholds), *interfacePath,
Cheng C Yange50345b2019-04-02 17:26:15 +0800431 findSensorType->second, factor, findProperty->second.maxReading,
432 findProperty->second.minReading);
Cheng C Yang209ec562019-03-12 16:37:44 +0800433 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800434
435 // OperationalStatus event
436 combineEvents[*psuName + "OperationalStatus"] =
437 std::make_unique<PSUCombineEvent>(
438 objectServer, io, *psuName, eventPathList, "OperationalStatus");
Cheng C Yang209ec562019-03-12 16:37:44 +0800439 }
440 return;
441}
442
Cheng C Yang916360b2019-05-07 18:47:16 +0800443void propertyInitialize(void)
Cheng C Yang209ec562019-03-12 16:37:44 +0800444{
Cheng C Yange50345b2019-04-02 17:26:15 +0800445 sensorTable = {{"power", "power/"},
446 {"curr", "current/"},
447 {"temp", "temperature/"},
448 {"in", "voltage/"},
449 {"fan", "fan_tach/"}};
450
451 labelMatch = {{"pin", PSUProperty("Input Power", 3000, 0, 6)},
452 {"pout1", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700453 {"pout2", PSUProperty("Output Power", 3000, 0, 6)},
454 {"pout3", PSUProperty("Output Power", 3000, 0, 6)},
Cheng C Yangbdbde962019-04-26 22:50:34 +0800455 {"vin", PSUProperty("Input Voltage", 300, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700456 {"vout1", PSUProperty("Output Voltage", 255, 0, 3)},
457 {"vout2", PSUProperty("Output Voltage", 255, 0, 3)},
458 {"vout3", PSUProperty("Output Voltage", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800459 {"iin", PSUProperty("Input Current", 20, 0, 3)},
460 {"iout1", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700461 {"iout2", PSUProperty("Output Current", 255, 0, 3)},
462 {"iout3", PSUProperty("Output Current", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800463 {"temp1", PSUProperty("Temperature", 127, -128, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700464 {"temp2", PSUProperty("Temperature", 127, -128, 3)},
465 {"temp3", PSUProperty("Temperature", 127, -128, 3)},
Cheng C Yang8dbb3952019-05-23 00:03:12 +0800466 {"fan1", PSUProperty("Fan Speed 1", 30000, 0, 0)},
467 {"fan2", PSUProperty("Fan Speed 2", 30000, 0, 0)}};
Cheng C Yang916360b2019-05-07 18:47:16 +0800468
469 pwmTable = {{"fan1", "Fan_1"}, {"fan2", "Fan_2"}};
Cheng C Yang58b2b532019-05-31 00:19:45 +0800470
471 limitEventMatch = {{"PredictiveFailure", {"max_alarm", "min_alarm"}},
472 {"Failure", {"crit_alarm", "lcrit_alarm"}}};
473
474 eventMatch = {
475 {"PredictiveFailure", {"power1_alarm"}},
476 {"Failure", {"in2_alarm"}},
477 {"ACLost", {"in1_alarm", "in1_lcrit_alarm"}},
478 {"FanFault", {"fan1_alarm", "fan2_alarm", "fan1_fault", "fan2_fault"}}};
Cheng C Yang209ec562019-03-12 16:37:44 +0800479}
480
481int main(int argc, char** argv)
482{
483 boost::asio::io_service io;
484 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
485
486 systemBus->request_name("xyz.openbmc_project.PSUSensor");
487 sdbusplus::asio::object_server objectServer(systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800488 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
Cheng C Yang209ec562019-03-12 16:37:44 +0800489
Cheng C Yang916360b2019-05-07 18:47:16 +0800490 propertyInitialize();
Cheng C Yang209ec562019-03-12 16:37:44 +0800491
Cheng C Yang916360b2019-05-07 18:47:16 +0800492 io.post([&]() { createSensors(io, objectServer, systemBus); });
Cheng C Yang209ec562019-03-12 16:37:44 +0800493 boost::asio::deadline_timer filterTimer(io);
494 std::function<void(sdbusplus::message::message&)> eventHandler =
495 [&](sdbusplus::message::message& message) {
496 if (message.is_method_error())
497 {
498 std::cerr << "callback method error\n";
499 return;
500 }
501 filterTimer.expires_from_now(boost::posix_time::seconds(1));
502 filterTimer.async_wait([&](const boost::system::error_code& ec) {
503 if (ec == boost::asio::error::operation_aborted)
504 {
505 return;
506 }
507 else if (ec)
508 {
509 std::cerr << "timer error\n";
510 }
Cheng C Yang916360b2019-05-07 18:47:16 +0800511 createSensors(io, objectServer, systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800512 });
513 };
514
515 for (const char* type : sensorTypes)
516 {
517 auto match = std::make_unique<sdbusplus::bus::match::match>(
518 static_cast<sdbusplus::bus::bus&>(*systemBus),
519 "type='signal',member='PropertiesChanged',path_namespace='" +
520 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
521 eventHandler);
522 matches.emplace_back(std::move(match));
523 }
524 io.run();
525}