blob: c8107bd031dd2133d9f4def85f52e1d24f8c5d44 [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
Patrick Ventureca44b2f2019-10-31 11:02:26 -070017#include "PSUEvent.hpp"
18
Cheng C Yang5665db22019-07-12 00:57:30 +080019#include <systemd/sd-journal.h>
20
Patrick Venture96e97db2019-10-31 13:44:38 -070021#include <boost/container/flat_map.hpp>
Cheng C Yang58b2b532019-05-31 00:19:45 +080022#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070023#include <memory>
Cheng C Yang58b2b532019-05-31 00:19:45 +080024#include <sdbusplus/asio/connection.hpp>
25#include <sdbusplus/asio/object_server.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070026#include <stdexcept>
27#include <string>
28#include <utility>
29#include <variant>
30#include <vector>
Cheng C Yang58b2b532019-05-31 00:19:45 +080031
32PSUCombineEvent::PSUCombineEvent(
Yong Li3046a022020-04-03 13:01:02 +080033 sdbusplus::asio::object_server& objectServer,
34 std::shared_ptr<sdbusplus::asio::connection>& conn,
35 boost::asio::io_service& io, const std::string& psuName,
Cheng C Yang58b2b532019-05-31 00:19:45 +080036 boost::container::flat_map<std::string, std::vector<std::string>>&
37 eventPathList,
Cheng C Yang202a1ff2020-01-09 09:34:22 +080038 boost::container::flat_map<
39 std::string,
40 boost::container::flat_map<std::string, std::vector<std::string>>>&
41 groupEventPathList,
Cheng C Yang58b2b532019-05-31 00:19:45 +080042 const std::string& combineEventName) :
43 objServer(objectServer)
44{
45 eventInterface = objServer.add_interface(
46 "/xyz/openbmc_project/State/Decorator/" + psuName + "_" +
47 combineEventName,
48 "xyz.openbmc_project.State.Decorator.OperationalStatus");
James Feistb6c0b912019-07-09 12:21:44 -070049 eventInterface->register_property("functional", true);
Cheng C Yang58b2b532019-05-31 00:19:45 +080050
51 if (!eventInterface->initialize())
52 {
53 std::cerr << "error initializing event interface\n";
54 }
55
56 std::shared_ptr<std::set<std::string>> combineEvent =
57 std::make_shared<std::set<std::string>>();
58 for (const auto& pathList : eventPathList)
59 {
Yong Li591b1e42019-12-19 17:46:31 +080060 std::shared_ptr<std::set<std::string>> assert =
61 std::make_shared<std::set<std::string>>();
Cheng C Yang202a1ff2020-01-09 09:34:22 +080062 std::shared_ptr<bool> state = std::make_shared<bool>(false);
Yong Li591b1e42019-12-19 17:46:31 +080063
Cheng C Yang58b2b532019-05-31 00:19:45 +080064 const std::string& eventName = pathList.first;
65 std::string eventPSUName = eventName + psuName;
66 for (const auto& path : pathList.second)
67 {
Yong Libf8b1da2020-04-15 16:32:50 +080068 auto p = std::make_shared<PSUSubEvent>(
Yong Li3046a022020-04-03 13:01:02 +080069 eventInterface, path, conn, io, eventName, eventName, assert,
Yong Libf8b1da2020-04-15 16:32:50 +080070 combineEvent, state, psuName);
71 p->setupRead();
72
73 events[eventPSUName].emplace_back(p);
Cheng C Yang58b2b532019-05-31 00:19:45 +080074 asserts.emplace_back(assert);
75 states.emplace_back(state);
76 }
77 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +080078
79 for (const auto& groupPathList : groupEventPathList)
80 {
81 for (const auto& pathList : groupPathList.second)
82 {
83 std::shared_ptr<std::set<std::string>> assert =
84 std::make_shared<std::set<std::string>>();
85 std::shared_ptr<bool> state = std::make_shared<bool>(false);
86
87 const std::string& groupEventName = pathList.first;
88 std::string eventPSUName = groupEventName + psuName;
89 for (const auto& path : pathList.second)
90 {
Yong Libf8b1da2020-04-15 16:32:50 +080091 auto p = std::make_shared<PSUSubEvent>(
Yong Li3046a022020-04-03 13:01:02 +080092 eventInterface, path, conn, io, groupEventName,
Yong Libf8b1da2020-04-15 16:32:50 +080093 groupPathList.first, assert, combineEvent, state, psuName);
94 p->setupRead();
95 events[eventPSUName].emplace_back(p);
96
Cheng C Yang202a1ff2020-01-09 09:34:22 +080097 asserts.emplace_back(assert);
98 states.emplace_back(state);
99 }
100 }
101 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800102}
103
104PSUCombineEvent::~PSUCombineEvent()
105{
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800106 // Clear unique_ptr first
107 for (auto& event : events)
108 {
109 for (auto& subEventPtr : event.second)
110 {
111 subEventPtr.reset();
112 }
113 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800114 events.clear();
115 objServer.remove_interface(eventInterface);
116}
117
Cheng C Yang9b53a622019-08-27 22:13:58 +0800118static boost::container::flat_map<std::string,
119 std::pair<std::string, std::string>>
120 logID = {
121 {"PredictiveFailure",
122 {"OpenBMC.0.1.PowerSupplyFailurePredicted",
123 "OpenBMC.0.1.PowerSupplyPredictedFailureRecovered"}},
124 {"Failure",
125 {"OpenBMC.0.1.PowerSupplyFailed", "OpenBMC.0.1.PowerSupplyRecovered"}},
126 {"ACLost",
Cheng C Yangbb732ef2019-09-18 16:25:58 +0800127 {"OpenBMC.0.1.PowerSupplyPowerLost",
128 "OpenBMC.0.1.PowerSupplyPowerRestored"}},
Cheng C Yang9b53a622019-08-27 22:13:58 +0800129 {"FanFault",
130 {"OpenBMC.0.1.PowerSupplyFanFailed",
131 "OpenBMC.0.1.PowerSupplyFanRecovered"}},
jayaprakash Mutyalad12f23e2019-12-03 23:03:52 +0000132 {"ConfigureError",
133 {"OpenBMC.0.1.PowerSupplyConfigurationError",
134 "OpenBMC.0.1.PowerSupplyConfigurationErrorRecovered"}}};
Cheng C Yang5665db22019-07-12 00:57:30 +0800135
Cheng C Yang58b2b532019-05-31 00:19:45 +0800136PSUSubEvent::PSUSubEvent(
137 std::shared_ptr<sdbusplus::asio::dbus_interface> eventInterface,
Yong Li3046a022020-04-03 13:01:02 +0800138 const std::string& path, std::shared_ptr<sdbusplus::asio::connection>& conn,
139 boost::asio::io_service& io, const std::string& groupEventName,
140 const std::string& eventName,
Cheng C Yang58b2b532019-05-31 00:19:45 +0800141 std::shared_ptr<std::set<std::string>> asserts,
142 std::shared_ptr<std::set<std::string>> combineEvent,
Cheng C Yang5665db22019-07-12 00:57:30 +0800143 std::shared_ptr<bool> state, const std::string& psuName) :
Yong Libf8b1da2020-04-15 16:32:50 +0800144 std::enable_shared_from_this<PSUSubEvent>(),
145 eventInterface(eventInterface), asserts(asserts),
146 combineEvent(combineEvent), assertState(state), errCount(0), path(path),
147 eventName(eventName), waitTimer(io), inputDev(io), psuName(psuName),
148 groupEventName(groupEventName), systemBus(conn)
Cheng C Yang58b2b532019-05-31 00:19:45 +0800149{
Cheng C Yanga97f1342020-02-11 15:10:41 +0800150 fd = open(path.c_str(), O_RDONLY);
151 if (fd < 0)
152 {
153 std::cerr << "PSU sub event failed to open file\n";
154 return;
155 }
156 inputDev.assign(fd);
157
Cheng C Yang5665db22019-07-12 00:57:30 +0800158 auto found = logID.find(eventName);
159 if (found == logID.end())
160 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800161 assertMessage.clear();
162 deassertMessage.clear();
Cheng C Yang5665db22019-07-12 00:57:30 +0800163 }
164 else
165 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800166 assertMessage = found->second.first;
167 deassertMessage = found->second.second;
Cheng C Yang5665db22019-07-12 00:57:30 +0800168 }
169
170 auto fanPos = path.find("fan");
171 if (fanPos != std::string::npos)
172 {
173 fanName = path.substr(fanPos);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800174 auto fanNamePos = fanName.find("_");
175 if (fanNamePos != std::string::npos)
176 {
177 fanName = fanName.substr(0, fanNamePos);
178 }
Cheng C Yang5665db22019-07-12 00:57:30 +0800179 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800180}
181
182void PSUSubEvent::setupRead(void)
183{
Yong Libf8b1da2020-04-15 16:32:50 +0800184 std::shared_ptr<boost::asio::streambuf> buffer =
185 std::make_shared<boost::asio::streambuf>();
186 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
187
Cheng C Yang58b2b532019-05-31 00:19:45 +0800188 boost::asio::async_read_until(
Yong Libf8b1da2020-04-15 16:32:50 +0800189 inputDev, *buffer, '\n',
190 [weakRef, buffer](const boost::system::error_code& ec,
191 std::size_t /*bytes_transfered*/) {
192 std::shared_ptr<PSUSubEvent> self = weakRef.lock();
193 if (self)
194 {
195 self->readBuf = buffer;
196 self->handleResponse(ec);
197 }
198 });
Cheng C Yang58b2b532019-05-31 00:19:45 +0800199}
200
201PSUSubEvent::~PSUSubEvent()
202{
Cheng C Yang58b2b532019-05-31 00:19:45 +0800203 waitTimer.cancel();
Cheng C Yanga97f1342020-02-11 15:10:41 +0800204 inputDev.close();
Cheng C Yang58b2b532019-05-31 00:19:45 +0800205}
206
207void PSUSubEvent::handleResponse(const boost::system::error_code& err)
208{
Yong Libf8b1da2020-04-15 16:32:50 +0800209 if ((err == boost::system::errc::bad_file_descriptor) ||
210 (err == boost::asio::error::misc_errors::not_found))
Cheng C Yang58b2b532019-05-31 00:19:45 +0800211 {
212 return;
213 }
Yong Libf8b1da2020-04-15 16:32:50 +0800214 std::istream responseStream(readBuf.get());
Cheng C Yang58b2b532019-05-31 00:19:45 +0800215 if (!err)
216 {
217 std::string response;
218 try
219 {
220 std::getline(responseStream, response);
Josh Lehan833661a2020-03-04 17:35:41 -0800221 int nvalue = std::stoi(response);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800222 responseStream.clear();
Josh Lehan833661a2020-03-04 17:35:41 -0800223
224 updateValue(nvalue);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800225 errCount = 0;
226 }
227 catch (const std::invalid_argument&)
228 {
229 errCount++;
230 }
231 }
232 else
233 {
234 errCount++;
235 }
236 if (errCount >= warnAfterErrorCount)
237 {
238 if (errCount == warnAfterErrorCount)
239 {
240 std::cerr << "Failure to read event at " << path << "\n";
241 }
242 updateValue(0);
243 errCount++;
244 }
Cheng C Yanga97f1342020-02-11 15:10:41 +0800245 lseek(fd, 0, SEEK_SET);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800246 waitTimer.expires_from_now(boost::posix_time::milliseconds(eventPollMs));
Yong Libf8b1da2020-04-15 16:32:50 +0800247
248 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
249 waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
250 std::shared_ptr<PSUSubEvent> self = weakRef.lock();
Cheng C Yang58b2b532019-05-31 00:19:45 +0800251 if (ec == boost::asio::error::operation_aborted)
252 {
253 return;
254 }
Yong Libf8b1da2020-04-15 16:32:50 +0800255 if (self)
256 {
257 self->setupRead();
258 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800259 });
260}
261
262// Any of the sub events of one event is asserted, then the event will be
263// asserted. Only if none of the sub events are asserted, the event will be
264// deasserted.
265void PSUSubEvent::updateValue(const int& newValue)
266{
Josh Lehan833661a2020-03-04 17:35:41 -0800267 // Take no action if value already equal
268 // Same semantics as Sensor::updateValue(const double&)
269 if (newValue == value)
270 {
271 return;
272 }
273
Cheng C Yang58b2b532019-05-31 00:19:45 +0800274 if (newValue == 0)
275 {
Yong Li591b1e42019-12-19 17:46:31 +0800276 // log deassert only after all asserts are gone
Cheng C Yang58b2b532019-05-31 00:19:45 +0800277 if (!(*asserts).empty())
278 {
Yong Li591b1e42019-12-19 17:46:31 +0800279 auto found = (*asserts).find(path);
280 if (found == (*asserts).end())
281 {
282 return;
283 }
284 (*asserts).erase(path);
285
Cheng C Yang58b2b532019-05-31 00:19:45 +0800286 return;
287 }
288 if (*assertState == true)
289 {
290 *assertState = false;
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800291 auto foundCombine = (*combineEvent).find(groupEventName);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800292 if (foundCombine == (*combineEvent).end())
Cheng C Yang58b2b532019-05-31 00:19:45 +0800293 {
294 return;
295 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800296 (*combineEvent).erase(groupEventName);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800297 if (!deassertMessage.empty())
298 {
299 // Fan Failed has two args
300 std::string sendMessage = eventName + " deassert";
301 if (deassertMessage == "OpenBMC.0.1.PowerSupplyFanRecovered")
302 {
303 sd_journal_send(
304 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800305 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
Cheng C Yang9b53a622019-08-27 22:13:58 +0800306 deassertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s,%s",
307 psuName.c_str(), fanName.c_str(), NULL);
308 }
309 else
310 {
311 sd_journal_send(
312 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800313 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
Cheng C Yang9b53a622019-08-27 22:13:58 +0800314 deassertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s",
315 psuName.c_str(), NULL);
316 }
317 }
318
Cheng C Yang58b2b532019-05-31 00:19:45 +0800319 if ((*combineEvent).empty())
320 {
321 eventInterface->set_property("functional", true);
322 }
323 }
324 }
325 else
326 {
Yong Li591b1e42019-12-19 17:46:31 +0800327 if ((*assertState == false) && ((*asserts).empty()))
Cheng C Yang58b2b532019-05-31 00:19:45 +0800328 {
329 *assertState = true;
Cheng C Yang9b53a622019-08-27 22:13:58 +0800330 if (!assertMessage.empty())
Cheng C Yang5665db22019-07-12 00:57:30 +0800331 {
Yong Li3046a022020-04-03 13:01:02 +0800332 // For failure and configure error, spec requires a beep
333 if ((assertMessage == "OpenBMC.0.1.PowerSupplyFailed") ||
334 (assertMessage ==
335 "OpenBMC.0.1.PowerSupplyConfigurationError"))
336 {
337 std::cout << " beep for " << assertMessage << "\n";
338 beep(beepPSUFailure);
339 }
340
Cheng C Yang5665db22019-07-12 00:57:30 +0800341 // Fan Failed has two args
342 std::string sendMessage = eventName + " assert";
Cheng C Yang9b53a622019-08-27 22:13:58 +0800343 if (assertMessage == "OpenBMC.0.1.PowerSupplyFanFailed")
Cheng C Yang5665db22019-07-12 00:57:30 +0800344 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800345 sd_journal_send(
346 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800347 LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
348 assertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s,%s",
349 psuName.c_str(), fanName.c_str(), NULL);
Cheng C Yang5665db22019-07-12 00:57:30 +0800350 }
351 else
352 {
353 sd_journal_send(
354 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800355 LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
356 assertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s",
357 psuName.c_str(), NULL);
Cheng C Yang5665db22019-07-12 00:57:30 +0800358 }
359 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800360 if ((*combineEvent).empty())
361 {
362 eventInterface->set_property("functional", false);
363 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800364 (*combineEvent).emplace(groupEventName);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800365 }
Yong Li591b1e42019-12-19 17:46:31 +0800366 (*asserts).emplace(path);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800367 }
368 value = newValue;
369}
Yong Li3046a022020-04-03 13:01:02 +0800370
371void PSUSubEvent::beep(const uint8_t& beepPriority)
372{
373 systemBus->async_method_call(
374 [](boost::system::error_code ec) {
375 if (ec)
376 {
377 std::cerr << "beep error (ec = " << ec << ")\n";
378 return;
379 }
380 },
381 "xyz.openbmc_project.BeepCode", "/xyz/openbmc_project/BeepCode",
382 "xyz.openbmc_project.BeepCode", "Beep", uint8_t(beepPriority));
383}