blob: 375c24debeb2495eb46c9dad13d622b17624e6d2 [file] [log] [blame]
Cheng C Yang58b2b532019-05-31 00:19:45 +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 Yang5665db22019-07-12 00:57:30 +080017#include <systemd/sd-journal.h>
18
Ed Tanous8a57ec02020-10-09 12:46:52 -070019#include <PSUEvent.hpp>
James Feist8086aba2020-08-25 16:00:59 -070020#include <boost/asio/io_service.hpp>
21#include <boost/asio/read_until.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070022#include <boost/container/flat_map.hpp>
Cheng C Yang58b2b532019-05-31 00:19:45 +080023#include <sdbusplus/asio/connection.hpp>
24#include <sdbusplus/asio/object_server.hpp>
James Feist38fb5982020-05-28 10:09:54 -070025
26#include <iostream>
27#include <memory>
Patrick Venture96e97db2019-10-31 13:44:38 -070028#include <stdexcept>
29#include <string>
30#include <utility>
31#include <variant>
32#include <vector>
Cheng C Yang58b2b532019-05-31 00:19:45 +080033
34PSUCombineEvent::PSUCombineEvent(
Yong Li3046a022020-04-03 13:01:02 +080035 sdbusplus::asio::object_server& objectServer,
36 std::shared_ptr<sdbusplus::asio::connection>& conn,
37 boost::asio::io_service& io, const std::string& psuName,
Cheng C Yang58b2b532019-05-31 00:19:45 +080038 boost::container::flat_map<std::string, std::vector<std::string>>&
39 eventPathList,
Cheng C Yang202a1ff2020-01-09 09:34:22 +080040 boost::container::flat_map<
41 std::string,
42 boost::container::flat_map<std::string, std::vector<std::string>>>&
43 groupEventPathList,
Cheng C Yang58b2b532019-05-31 00:19:45 +080044 const std::string& combineEventName) :
45 objServer(objectServer)
46{
47 eventInterface = objServer.add_interface(
48 "/xyz/openbmc_project/State/Decorator/" + psuName + "_" +
49 combineEventName,
50 "xyz.openbmc_project.State.Decorator.OperationalStatus");
James Feistb6c0b912019-07-09 12:21:44 -070051 eventInterface->register_property("functional", true);
Cheng C Yang58b2b532019-05-31 00:19:45 +080052
53 if (!eventInterface->initialize())
54 {
55 std::cerr << "error initializing event interface\n";
56 }
57
58 std::shared_ptr<std::set<std::string>> combineEvent =
59 std::make_shared<std::set<std::string>>();
60 for (const auto& pathList : eventPathList)
61 {
Yong Li591b1e42019-12-19 17:46:31 +080062 std::shared_ptr<std::set<std::string>> assert =
63 std::make_shared<std::set<std::string>>();
Cheng C Yang202a1ff2020-01-09 09:34:22 +080064 std::shared_ptr<bool> state = std::make_shared<bool>(false);
Yong Li591b1e42019-12-19 17:46:31 +080065
Cheng C Yang58b2b532019-05-31 00:19:45 +080066 const std::string& eventName = pathList.first;
67 std::string eventPSUName = eventName + psuName;
68 for (const auto& path : pathList.second)
69 {
Yong Libf8b1da2020-04-15 16:32:50 +080070 auto p = std::make_shared<PSUSubEvent>(
Yong Li3046a022020-04-03 13:01:02 +080071 eventInterface, path, conn, io, eventName, eventName, assert,
Yong Libf8b1da2020-04-15 16:32:50 +080072 combineEvent, state, psuName);
73 p->setupRead();
74
75 events[eventPSUName].emplace_back(p);
Cheng C Yang58b2b532019-05-31 00:19:45 +080076 asserts.emplace_back(assert);
77 states.emplace_back(state);
78 }
79 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +080080
81 for (const auto& groupPathList : groupEventPathList)
82 {
83 for (const auto& pathList : groupPathList.second)
84 {
85 std::shared_ptr<std::set<std::string>> assert =
86 std::make_shared<std::set<std::string>>();
87 std::shared_ptr<bool> state = std::make_shared<bool>(false);
88
89 const std::string& groupEventName = pathList.first;
90 std::string eventPSUName = groupEventName + psuName;
91 for (const auto& path : pathList.second)
92 {
Yong Libf8b1da2020-04-15 16:32:50 +080093 auto p = std::make_shared<PSUSubEvent>(
Yong Li3046a022020-04-03 13:01:02 +080094 eventInterface, path, conn, io, groupEventName,
Yong Libf8b1da2020-04-15 16:32:50 +080095 groupPathList.first, assert, combineEvent, state, psuName);
96 p->setupRead();
97 events[eventPSUName].emplace_back(p);
98
Cheng C Yang202a1ff2020-01-09 09:34:22 +080099 asserts.emplace_back(assert);
100 states.emplace_back(state);
101 }
102 }
103 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800104}
105
106PSUCombineEvent::~PSUCombineEvent()
107{
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800108 // Clear unique_ptr first
109 for (auto& event : events)
110 {
111 for (auto& subEventPtr : event.second)
112 {
113 subEventPtr.reset();
114 }
115 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800116 events.clear();
117 objServer.remove_interface(eventInterface);
118}
119
Cheng C Yang9b53a622019-08-27 22:13:58 +0800120static boost::container::flat_map<std::string,
121 std::pair<std::string, std::string>>
122 logID = {
123 {"PredictiveFailure",
124 {"OpenBMC.0.1.PowerSupplyFailurePredicted",
125 "OpenBMC.0.1.PowerSupplyPredictedFailureRecovered"}},
126 {"Failure",
127 {"OpenBMC.0.1.PowerSupplyFailed", "OpenBMC.0.1.PowerSupplyRecovered"}},
128 {"ACLost",
Cheng C Yangbb732ef2019-09-18 16:25:58 +0800129 {"OpenBMC.0.1.PowerSupplyPowerLost",
130 "OpenBMC.0.1.PowerSupplyPowerRestored"}},
Cheng C Yang9b53a622019-08-27 22:13:58 +0800131 {"FanFault",
132 {"OpenBMC.0.1.PowerSupplyFanFailed",
133 "OpenBMC.0.1.PowerSupplyFanRecovered"}},
jayaprakash Mutyalad12f23e2019-12-03 23:03:52 +0000134 {"ConfigureError",
135 {"OpenBMC.0.1.PowerSupplyConfigurationError",
136 "OpenBMC.0.1.PowerSupplyConfigurationErrorRecovered"}}};
Cheng C Yang5665db22019-07-12 00:57:30 +0800137
Cheng C Yang58b2b532019-05-31 00:19:45 +0800138PSUSubEvent::PSUSubEvent(
139 std::shared_ptr<sdbusplus::asio::dbus_interface> eventInterface,
Yong Li3046a022020-04-03 13:01:02 +0800140 const std::string& path, std::shared_ptr<sdbusplus::asio::connection>& conn,
141 boost::asio::io_service& io, const std::string& groupEventName,
142 const std::string& eventName,
Cheng C Yang58b2b532019-05-31 00:19:45 +0800143 std::shared_ptr<std::set<std::string>> asserts,
144 std::shared_ptr<std::set<std::string>> combineEvent,
Cheng C Yang5665db22019-07-12 00:57:30 +0800145 std::shared_ptr<bool> state, const std::string& psuName) :
Yong Libf8b1da2020-04-15 16:32:50 +0800146 std::enable_shared_from_this<PSUSubEvent>(),
Ed Tanous8a57ec02020-10-09 12:46:52 -0700147 eventInterface(std::move(eventInterface)), asserts(std::move(asserts)),
148 combineEvent(std::move(combineEvent)), assertState(std::move(state)),
149 errCount(0), path(path), eventName(eventName), waitTimer(io), inputDev(io),
150 psuName(psuName), groupEventName(groupEventName), systemBus(conn)
Cheng C Yang58b2b532019-05-31 00:19:45 +0800151{
Cheng C Yanga97f1342020-02-11 15:10:41 +0800152 fd = open(path.c_str(), O_RDONLY);
153 if (fd < 0)
154 {
155 std::cerr << "PSU sub event failed to open file\n";
156 return;
157 }
158 inputDev.assign(fd);
159
Cheng C Yang5665db22019-07-12 00:57:30 +0800160 auto found = logID.find(eventName);
161 if (found == logID.end())
162 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800163 assertMessage.clear();
164 deassertMessage.clear();
Cheng C Yang5665db22019-07-12 00:57:30 +0800165 }
166 else
167 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800168 assertMessage = found->second.first;
169 deassertMessage = found->second.second;
Cheng C Yang5665db22019-07-12 00:57:30 +0800170 }
171
172 auto fanPos = path.find("fan");
173 if (fanPos != std::string::npos)
174 {
175 fanName = path.substr(fanPos);
Ed Tanous8a57ec02020-10-09 12:46:52 -0700176 auto fanNamePos = fanName.find('_');
Cheng C Yang9b53a622019-08-27 22:13:58 +0800177 if (fanNamePos != std::string::npos)
178 {
179 fanName = fanName.substr(0, fanNamePos);
180 }
Cheng C Yang5665db22019-07-12 00:57:30 +0800181 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800182}
183
184void PSUSubEvent::setupRead(void)
185{
Yong Libf8b1da2020-04-15 16:32:50 +0800186 std::shared_ptr<boost::asio::streambuf> buffer =
187 std::make_shared<boost::asio::streambuf>();
188 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
189
Cheng C Yang58b2b532019-05-31 00:19:45 +0800190 boost::asio::async_read_until(
Yong Libf8b1da2020-04-15 16:32:50 +0800191 inputDev, *buffer, '\n',
192 [weakRef, buffer](const boost::system::error_code& ec,
193 std::size_t /*bytes_transfered*/) {
194 std::shared_ptr<PSUSubEvent> self = weakRef.lock();
195 if (self)
196 {
197 self->readBuf = buffer;
198 self->handleResponse(ec);
199 }
200 });
Cheng C Yang58b2b532019-05-31 00:19:45 +0800201}
202
203PSUSubEvent::~PSUSubEvent()
204{
Cheng C Yang58b2b532019-05-31 00:19:45 +0800205 waitTimer.cancel();
Cheng C Yanga97f1342020-02-11 15:10:41 +0800206 inputDev.close();
Cheng C Yang58b2b532019-05-31 00:19:45 +0800207}
208
209void PSUSubEvent::handleResponse(const boost::system::error_code& err)
210{
Yong Libf8b1da2020-04-15 16:32:50 +0800211 if ((err == boost::system::errc::bad_file_descriptor) ||
212 (err == boost::asio::error::misc_errors::not_found))
Cheng C Yang58b2b532019-05-31 00:19:45 +0800213 {
214 return;
215 }
Yong Libf8b1da2020-04-15 16:32:50 +0800216 std::istream responseStream(readBuf.get());
Cheng C Yang58b2b532019-05-31 00:19:45 +0800217 if (!err)
218 {
219 std::string response;
220 try
221 {
222 std::getline(responseStream, response);
Josh Lehan833661a2020-03-04 17:35:41 -0800223 int nvalue = std::stoi(response);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800224 responseStream.clear();
Josh Lehan833661a2020-03-04 17:35:41 -0800225
226 updateValue(nvalue);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800227 errCount = 0;
228 }
229 catch (const std::invalid_argument&)
230 {
231 errCount++;
232 }
233 }
234 else
235 {
236 errCount++;
237 }
238 if (errCount >= warnAfterErrorCount)
239 {
240 if (errCount == warnAfterErrorCount)
241 {
242 std::cerr << "Failure to read event at " << path << "\n";
243 }
244 updateValue(0);
245 errCount++;
246 }
Cheng C Yanga97f1342020-02-11 15:10:41 +0800247 lseek(fd, 0, SEEK_SET);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800248 waitTimer.expires_from_now(boost::posix_time::milliseconds(eventPollMs));
Yong Libf8b1da2020-04-15 16:32:50 +0800249
250 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
251 waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
252 std::shared_ptr<PSUSubEvent> self = weakRef.lock();
Cheng C Yang58b2b532019-05-31 00:19:45 +0800253 if (ec == boost::asio::error::operation_aborted)
254 {
255 return;
256 }
Yong Libf8b1da2020-04-15 16:32:50 +0800257 if (self)
258 {
259 self->setupRead();
260 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800261 });
262}
263
264// Any of the sub events of one event is asserted, then the event will be
265// asserted. Only if none of the sub events are asserted, the event will be
266// deasserted.
267void PSUSubEvent::updateValue(const int& newValue)
268{
Josh Lehan833661a2020-03-04 17:35:41 -0800269 // Take no action if value already equal
270 // Same semantics as Sensor::updateValue(const double&)
271 if (newValue == value)
272 {
273 return;
274 }
275
Cheng C Yang58b2b532019-05-31 00:19:45 +0800276 if (newValue == 0)
277 {
Yong Li591b1e42019-12-19 17:46:31 +0800278 // log deassert only after all asserts are gone
Cheng C Yang58b2b532019-05-31 00:19:45 +0800279 if (!(*asserts).empty())
280 {
Yong Li591b1e42019-12-19 17:46:31 +0800281 auto found = (*asserts).find(path);
282 if (found == (*asserts).end())
283 {
284 return;
285 }
286 (*asserts).erase(path);
287
Cheng C Yang58b2b532019-05-31 00:19:45 +0800288 return;
289 }
290 if (*assertState == true)
291 {
292 *assertState = false;
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800293 auto foundCombine = (*combineEvent).find(groupEventName);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800294 if (foundCombine == (*combineEvent).end())
Cheng C Yang58b2b532019-05-31 00:19:45 +0800295 {
296 return;
297 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800298 (*combineEvent).erase(groupEventName);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800299 if (!deassertMessage.empty())
300 {
301 // Fan Failed has two args
302 std::string sendMessage = eventName + " deassert";
303 if (deassertMessage == "OpenBMC.0.1.PowerSupplyFanRecovered")
304 {
305 sd_journal_send(
306 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800307 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
Cheng C Yang9b53a622019-08-27 22:13:58 +0800308 deassertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s,%s",
309 psuName.c_str(), fanName.c_str(), NULL);
310 }
311 else
312 {
313 sd_journal_send(
314 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800315 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
Cheng C Yang9b53a622019-08-27 22:13:58 +0800316 deassertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s",
317 psuName.c_str(), NULL);
318 }
319 }
320
Cheng C Yang58b2b532019-05-31 00:19:45 +0800321 if ((*combineEvent).empty())
322 {
323 eventInterface->set_property("functional", true);
324 }
325 }
326 }
327 else
328 {
Yong Li591b1e42019-12-19 17:46:31 +0800329 if ((*assertState == false) && ((*asserts).empty()))
Cheng C Yang58b2b532019-05-31 00:19:45 +0800330 {
331 *assertState = true;
Cheng C Yang9b53a622019-08-27 22:13:58 +0800332 if (!assertMessage.empty())
Cheng C Yang5665db22019-07-12 00:57:30 +0800333 {
Yong Li3046a022020-04-03 13:01:02 +0800334 // For failure and configure error, spec requires a beep
335 if ((assertMessage == "OpenBMC.0.1.PowerSupplyFailed") ||
336 (assertMessage ==
337 "OpenBMC.0.1.PowerSupplyConfigurationError"))
338 {
339 std::cout << " beep for " << assertMessage << "\n";
340 beep(beepPSUFailure);
341 }
342
Cheng C Yang5665db22019-07-12 00:57:30 +0800343 // Fan Failed has two args
344 std::string sendMessage = eventName + " assert";
Cheng C Yang9b53a622019-08-27 22:13:58 +0800345 if (assertMessage == "OpenBMC.0.1.PowerSupplyFanFailed")
Cheng C Yang5665db22019-07-12 00:57:30 +0800346 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800347 sd_journal_send(
348 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800349 LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
350 assertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s,%s",
351 psuName.c_str(), fanName.c_str(), NULL);
Cheng C Yang5665db22019-07-12 00:57:30 +0800352 }
353 else
354 {
355 sd_journal_send(
356 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800357 LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
358 assertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s",
359 psuName.c_str(), NULL);
Cheng C Yang5665db22019-07-12 00:57:30 +0800360 }
361 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800362 if ((*combineEvent).empty())
363 {
364 eventInterface->set_property("functional", false);
365 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800366 (*combineEvent).emplace(groupEventName);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800367 }
Yong Li591b1e42019-12-19 17:46:31 +0800368 (*asserts).emplace(path);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800369 }
370 value = newValue;
371}
Yong Li3046a022020-04-03 13:01:02 +0800372
373void PSUSubEvent::beep(const uint8_t& beepPriority)
374{
375 systemBus->async_method_call(
376 [](boost::system::error_code ec) {
377 if (ec)
378 {
379 std::cerr << "beep error (ec = " << ec << ")\n";
380 return;
381 }
382 },
383 "xyz.openbmc_project.BeepCode", "/xyz/openbmc_project/BeepCode",
384 "xyz.openbmc_project.BeepCode", "Beep", uint8_t(beepPriority));
385}