blob: 426691828b8dc102ff443ff48306b87b1072130d [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
James Feist8086aba2020-08-25 16:00:59 -070021#include <boost/asio/io_service.hpp>
22#include <boost/asio/read_until.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070023#include <boost/container/flat_map.hpp>
Cheng C Yang58b2b532019-05-31 00:19:45 +080024#include <sdbusplus/asio/connection.hpp>
25#include <sdbusplus/asio/object_server.hpp>
James Feist38fb5982020-05-28 10:09:54 -070026
27#include <iostream>
28#include <memory>
Patrick Venture96e97db2019-10-31 13:44:38 -070029#include <stdexcept>
30#include <string>
31#include <utility>
32#include <variant>
33#include <vector>
Cheng C Yang58b2b532019-05-31 00:19:45 +080034
35PSUCombineEvent::PSUCombineEvent(
Yong Li3046a022020-04-03 13:01:02 +080036 sdbusplus::asio::object_server& objectServer,
37 std::shared_ptr<sdbusplus::asio::connection>& conn,
38 boost::asio::io_service& io, const std::string& psuName,
Cheng C Yang58b2b532019-05-31 00:19:45 +080039 boost::container::flat_map<std::string, std::vector<std::string>>&
40 eventPathList,
Cheng C Yang202a1ff2020-01-09 09:34:22 +080041 boost::container::flat_map<
42 std::string,
43 boost::container::flat_map<std::string, std::vector<std::string>>>&
44 groupEventPathList,
Cheng C Yang58b2b532019-05-31 00:19:45 +080045 const std::string& combineEventName) :
46 objServer(objectServer)
47{
48 eventInterface = objServer.add_interface(
49 "/xyz/openbmc_project/State/Decorator/" + psuName + "_" +
50 combineEventName,
51 "xyz.openbmc_project.State.Decorator.OperationalStatus");
James Feistb6c0b912019-07-09 12:21:44 -070052 eventInterface->register_property("functional", true);
Cheng C Yang58b2b532019-05-31 00:19:45 +080053
54 if (!eventInterface->initialize())
55 {
56 std::cerr << "error initializing event interface\n";
57 }
58
59 std::shared_ptr<std::set<std::string>> combineEvent =
60 std::make_shared<std::set<std::string>>();
61 for (const auto& pathList : eventPathList)
62 {
Yong Li591b1e42019-12-19 17:46:31 +080063 std::shared_ptr<std::set<std::string>> assert =
64 std::make_shared<std::set<std::string>>();
Cheng C Yang202a1ff2020-01-09 09:34:22 +080065 std::shared_ptr<bool> state = std::make_shared<bool>(false);
Yong Li591b1e42019-12-19 17:46:31 +080066
Cheng C Yang58b2b532019-05-31 00:19:45 +080067 const std::string& eventName = pathList.first;
68 std::string eventPSUName = eventName + psuName;
69 for (const auto& path : pathList.second)
70 {
Yong Libf8b1da2020-04-15 16:32:50 +080071 auto p = std::make_shared<PSUSubEvent>(
Yong Li3046a022020-04-03 13:01:02 +080072 eventInterface, path, conn, io, eventName, eventName, assert,
Yong Libf8b1da2020-04-15 16:32:50 +080073 combineEvent, state, psuName);
74 p->setupRead();
75
76 events[eventPSUName].emplace_back(p);
Cheng C Yang58b2b532019-05-31 00:19:45 +080077 asserts.emplace_back(assert);
78 states.emplace_back(state);
79 }
80 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +080081
82 for (const auto& groupPathList : groupEventPathList)
83 {
84 for (const auto& pathList : groupPathList.second)
85 {
86 std::shared_ptr<std::set<std::string>> assert =
87 std::make_shared<std::set<std::string>>();
88 std::shared_ptr<bool> state = std::make_shared<bool>(false);
89
90 const std::string& groupEventName = pathList.first;
91 std::string eventPSUName = groupEventName + psuName;
92 for (const auto& path : pathList.second)
93 {
Yong Libf8b1da2020-04-15 16:32:50 +080094 auto p = std::make_shared<PSUSubEvent>(
Yong Li3046a022020-04-03 13:01:02 +080095 eventInterface, path, conn, io, groupEventName,
Yong Libf8b1da2020-04-15 16:32:50 +080096 groupPathList.first, assert, combineEvent, state, psuName);
97 p->setupRead();
98 events[eventPSUName].emplace_back(p);
99
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800100 asserts.emplace_back(assert);
101 states.emplace_back(state);
102 }
103 }
104 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800105}
106
107PSUCombineEvent::~PSUCombineEvent()
108{
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800109 // Clear unique_ptr first
110 for (auto& event : events)
111 {
112 for (auto& subEventPtr : event.second)
113 {
114 subEventPtr.reset();
115 }
116 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800117 events.clear();
118 objServer.remove_interface(eventInterface);
119}
120
Cheng C Yang9b53a622019-08-27 22:13:58 +0800121static boost::container::flat_map<std::string,
122 std::pair<std::string, std::string>>
123 logID = {
124 {"PredictiveFailure",
125 {"OpenBMC.0.1.PowerSupplyFailurePredicted",
126 "OpenBMC.0.1.PowerSupplyPredictedFailureRecovered"}},
127 {"Failure",
128 {"OpenBMC.0.1.PowerSupplyFailed", "OpenBMC.0.1.PowerSupplyRecovered"}},
129 {"ACLost",
Cheng C Yangbb732ef2019-09-18 16:25:58 +0800130 {"OpenBMC.0.1.PowerSupplyPowerLost",
131 "OpenBMC.0.1.PowerSupplyPowerRestored"}},
Cheng C Yang9b53a622019-08-27 22:13:58 +0800132 {"FanFault",
133 {"OpenBMC.0.1.PowerSupplyFanFailed",
134 "OpenBMC.0.1.PowerSupplyFanRecovered"}},
jayaprakash Mutyalad12f23e2019-12-03 23:03:52 +0000135 {"ConfigureError",
136 {"OpenBMC.0.1.PowerSupplyConfigurationError",
137 "OpenBMC.0.1.PowerSupplyConfigurationErrorRecovered"}}};
Cheng C Yang5665db22019-07-12 00:57:30 +0800138
Cheng C Yang58b2b532019-05-31 00:19:45 +0800139PSUSubEvent::PSUSubEvent(
140 std::shared_ptr<sdbusplus::asio::dbus_interface> eventInterface,
Yong Li3046a022020-04-03 13:01:02 +0800141 const std::string& path, std::shared_ptr<sdbusplus::asio::connection>& conn,
142 boost::asio::io_service& io, const std::string& groupEventName,
143 const std::string& eventName,
Cheng C Yang58b2b532019-05-31 00:19:45 +0800144 std::shared_ptr<std::set<std::string>> asserts,
145 std::shared_ptr<std::set<std::string>> combineEvent,
Cheng C Yang5665db22019-07-12 00:57:30 +0800146 std::shared_ptr<bool> state, const std::string& psuName) :
Yong Libf8b1da2020-04-15 16:32:50 +0800147 std::enable_shared_from_this<PSUSubEvent>(),
148 eventInterface(eventInterface), asserts(asserts),
149 combineEvent(combineEvent), assertState(state), errCount(0), path(path),
150 eventName(eventName), waitTimer(io), inputDev(io), psuName(psuName),
151 groupEventName(groupEventName), systemBus(conn)
Cheng C Yang58b2b532019-05-31 00:19:45 +0800152{
Cheng C Yanga97f1342020-02-11 15:10:41 +0800153 fd = open(path.c_str(), O_RDONLY);
154 if (fd < 0)
155 {
156 std::cerr << "PSU sub event failed to open file\n";
157 return;
158 }
159 inputDev.assign(fd);
160
Cheng C Yang5665db22019-07-12 00:57:30 +0800161 auto found = logID.find(eventName);
162 if (found == logID.end())
163 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800164 assertMessage.clear();
165 deassertMessage.clear();
Cheng C Yang5665db22019-07-12 00:57:30 +0800166 }
167 else
168 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800169 assertMessage = found->second.first;
170 deassertMessage = found->second.second;
Cheng C Yang5665db22019-07-12 00:57:30 +0800171 }
172
173 auto fanPos = path.find("fan");
174 if (fanPos != std::string::npos)
175 {
176 fanName = path.substr(fanPos);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800177 auto fanNamePos = fanName.find("_");
178 if (fanNamePos != std::string::npos)
179 {
180 fanName = fanName.substr(0, fanNamePos);
181 }
Cheng C Yang5665db22019-07-12 00:57:30 +0800182 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800183}
184
185void PSUSubEvent::setupRead(void)
186{
Yong Libf8b1da2020-04-15 16:32:50 +0800187 std::shared_ptr<boost::asio::streambuf> buffer =
188 std::make_shared<boost::asio::streambuf>();
189 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
190
Cheng C Yang58b2b532019-05-31 00:19:45 +0800191 boost::asio::async_read_until(
Yong Libf8b1da2020-04-15 16:32:50 +0800192 inputDev, *buffer, '\n',
193 [weakRef, buffer](const boost::system::error_code& ec,
194 std::size_t /*bytes_transfered*/) {
195 std::shared_ptr<PSUSubEvent> self = weakRef.lock();
196 if (self)
197 {
198 self->readBuf = buffer;
199 self->handleResponse(ec);
200 }
201 });
Cheng C Yang58b2b532019-05-31 00:19:45 +0800202}
203
204PSUSubEvent::~PSUSubEvent()
205{
Cheng C Yang58b2b532019-05-31 00:19:45 +0800206 waitTimer.cancel();
Cheng C Yanga97f1342020-02-11 15:10:41 +0800207 inputDev.close();
Cheng C Yang58b2b532019-05-31 00:19:45 +0800208}
209
210void PSUSubEvent::handleResponse(const boost::system::error_code& err)
211{
Yong Libf8b1da2020-04-15 16:32:50 +0800212 if ((err == boost::system::errc::bad_file_descriptor) ||
213 (err == boost::asio::error::misc_errors::not_found))
Cheng C Yang58b2b532019-05-31 00:19:45 +0800214 {
215 return;
216 }
Yong Libf8b1da2020-04-15 16:32:50 +0800217 std::istream responseStream(readBuf.get());
Cheng C Yang58b2b532019-05-31 00:19:45 +0800218 if (!err)
219 {
220 std::string response;
221 try
222 {
223 std::getline(responseStream, response);
Josh Lehan833661a2020-03-04 17:35:41 -0800224 int nvalue = std::stoi(response);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800225 responseStream.clear();
Josh Lehan833661a2020-03-04 17:35:41 -0800226
227 updateValue(nvalue);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800228 errCount = 0;
229 }
230 catch (const std::invalid_argument&)
231 {
232 errCount++;
233 }
234 }
235 else
236 {
237 errCount++;
238 }
239 if (errCount >= warnAfterErrorCount)
240 {
241 if (errCount == warnAfterErrorCount)
242 {
243 std::cerr << "Failure to read event at " << path << "\n";
244 }
245 updateValue(0);
246 errCount++;
247 }
Cheng C Yanga97f1342020-02-11 15:10:41 +0800248 lseek(fd, 0, SEEK_SET);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800249 waitTimer.expires_from_now(boost::posix_time::milliseconds(eventPollMs));
Yong Libf8b1da2020-04-15 16:32:50 +0800250
251 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
252 waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
253 std::shared_ptr<PSUSubEvent> self = weakRef.lock();
Cheng C Yang58b2b532019-05-31 00:19:45 +0800254 if (ec == boost::asio::error::operation_aborted)
255 {
256 return;
257 }
Yong Libf8b1da2020-04-15 16:32:50 +0800258 if (self)
259 {
260 self->setupRead();
261 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800262 });
263}
264
265// Any of the sub events of one event is asserted, then the event will be
266// asserted. Only if none of the sub events are asserted, the event will be
267// deasserted.
268void PSUSubEvent::updateValue(const int& newValue)
269{
Josh Lehan833661a2020-03-04 17:35:41 -0800270 // Take no action if value already equal
271 // Same semantics as Sensor::updateValue(const double&)
272 if (newValue == value)
273 {
274 return;
275 }
276
Cheng C Yang58b2b532019-05-31 00:19:45 +0800277 if (newValue == 0)
278 {
Yong Li591b1e42019-12-19 17:46:31 +0800279 // log deassert only after all asserts are gone
Cheng C Yang58b2b532019-05-31 00:19:45 +0800280 if (!(*asserts).empty())
281 {
Yong Li591b1e42019-12-19 17:46:31 +0800282 auto found = (*asserts).find(path);
283 if (found == (*asserts).end())
284 {
285 return;
286 }
287 (*asserts).erase(path);
288
Cheng C Yang58b2b532019-05-31 00:19:45 +0800289 return;
290 }
291 if (*assertState == true)
292 {
293 *assertState = false;
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800294 auto foundCombine = (*combineEvent).find(groupEventName);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800295 if (foundCombine == (*combineEvent).end())
Cheng C Yang58b2b532019-05-31 00:19:45 +0800296 {
297 return;
298 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800299 (*combineEvent).erase(groupEventName);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800300 if (!deassertMessage.empty())
301 {
302 // Fan Failed has two args
303 std::string sendMessage = eventName + " deassert";
304 if (deassertMessage == "OpenBMC.0.1.PowerSupplyFanRecovered")
305 {
306 sd_journal_send(
307 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800308 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
Cheng C Yang9b53a622019-08-27 22:13:58 +0800309 deassertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s,%s",
310 psuName.c_str(), fanName.c_str(), NULL);
311 }
312 else
313 {
314 sd_journal_send(
315 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800316 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
Cheng C Yang9b53a622019-08-27 22:13:58 +0800317 deassertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s",
318 psuName.c_str(), NULL);
319 }
320 }
321
Cheng C Yang58b2b532019-05-31 00:19:45 +0800322 if ((*combineEvent).empty())
323 {
324 eventInterface->set_property("functional", true);
325 }
326 }
327 }
328 else
329 {
Yong Li591b1e42019-12-19 17:46:31 +0800330 if ((*assertState == false) && ((*asserts).empty()))
Cheng C Yang58b2b532019-05-31 00:19:45 +0800331 {
332 *assertState = true;
Cheng C Yang9b53a622019-08-27 22:13:58 +0800333 if (!assertMessage.empty())
Cheng C Yang5665db22019-07-12 00:57:30 +0800334 {
Yong Li3046a022020-04-03 13:01:02 +0800335 // For failure and configure error, spec requires a beep
336 if ((assertMessage == "OpenBMC.0.1.PowerSupplyFailed") ||
337 (assertMessage ==
338 "OpenBMC.0.1.PowerSupplyConfigurationError"))
339 {
340 std::cout << " beep for " << assertMessage << "\n";
341 beep(beepPSUFailure);
342 }
343
Cheng C Yang5665db22019-07-12 00:57:30 +0800344 // Fan Failed has two args
345 std::string sendMessage = eventName + " assert";
Cheng C Yang9b53a622019-08-27 22:13:58 +0800346 if (assertMessage == "OpenBMC.0.1.PowerSupplyFanFailed")
Cheng C Yang5665db22019-07-12 00:57:30 +0800347 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800348 sd_journal_send(
349 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800350 LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
351 assertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s,%s",
352 psuName.c_str(), fanName.c_str(), NULL);
Cheng C Yang5665db22019-07-12 00:57:30 +0800353 }
354 else
355 {
356 sd_journal_send(
357 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800358 LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
359 assertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s",
360 psuName.c_str(), NULL);
Cheng C Yang5665db22019-07-12 00:57:30 +0800361 }
362 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800363 if ((*combineEvent).empty())
364 {
365 eventInterface->set_property("functional", false);
366 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800367 (*combineEvent).emplace(groupEventName);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800368 }
Yong Li591b1e42019-12-19 17:46:31 +0800369 (*asserts).emplace(path);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800370 }
371 value = newValue;
372}
Yong Li3046a022020-04-03 13:01:02 +0800373
374void PSUSubEvent::beep(const uint8_t& beepPriority)
375{
376 systemBus->async_method_call(
377 [](boost::system::error_code ec) {
378 if (ec)
379 {
380 std::cerr << "beep error (ec = " << ec << ")\n";
381 return;
382 }
383 },
384 "xyz.openbmc_project.BeepCode", "/xyz/openbmc_project/BeepCode",
385 "xyz.openbmc_project.BeepCode", "Beep", uint8_t(beepPriority));
386}