blob: 94125fa4ccf87918020ba47913ece7e5d6b94829 [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 <sdbusplus/asio/connection.hpp>
23#include <sdbusplus/asio/object_server.hpp>
James Feist38fb5982020-05-28 10:09:54 -070024
25#include <iostream>
26#include <memory>
Patrick Venture96e97db2019-10-31 13:44:38 -070027#include <stdexcept>
28#include <string>
29#include <utility>
30#include <variant>
31#include <vector>
Cheng C Yang58b2b532019-05-31 00:19:45 +080032
33PSUCombineEvent::PSUCombineEvent(
Yong Li3046a022020-04-03 13:01:02 +080034 sdbusplus::asio::object_server& objectServer,
35 std::shared_ptr<sdbusplus::asio::connection>& conn,
36 boost::asio::io_service& io, const std::string& psuName,
Cheng C Yang58b2b532019-05-31 00:19:45 +080037 boost::container::flat_map<std::string, std::vector<std::string>>&
38 eventPathList,
Cheng C Yang202a1ff2020-01-09 09:34:22 +080039 boost::container::flat_map<
40 std::string,
41 boost::container::flat_map<std::string, std::vector<std::string>>>&
42 groupEventPathList,
Cheng C Yang58b2b532019-05-31 00:19:45 +080043 const std::string& combineEventName) :
44 objServer(objectServer)
45{
46 eventInterface = objServer.add_interface(
47 "/xyz/openbmc_project/State/Decorator/" + psuName + "_" +
48 combineEventName,
49 "xyz.openbmc_project.State.Decorator.OperationalStatus");
James Feistb6c0b912019-07-09 12:21:44 -070050 eventInterface->register_property("functional", true);
Cheng C Yang58b2b532019-05-31 00:19:45 +080051
52 if (!eventInterface->initialize())
53 {
54 std::cerr << "error initializing event interface\n";
55 }
56
57 std::shared_ptr<std::set<std::string>> combineEvent =
58 std::make_shared<std::set<std::string>>();
59 for (const auto& pathList : eventPathList)
60 {
Yong Li591b1e42019-12-19 17:46:31 +080061 std::shared_ptr<std::set<std::string>> assert =
62 std::make_shared<std::set<std::string>>();
Cheng C Yang202a1ff2020-01-09 09:34:22 +080063 std::shared_ptr<bool> state = std::make_shared<bool>(false);
Yong Li591b1e42019-12-19 17:46:31 +080064
Cheng C Yang58b2b532019-05-31 00:19:45 +080065 const std::string& eventName = pathList.first;
66 std::string eventPSUName = eventName + psuName;
67 for (const auto& path : pathList.second)
68 {
Yong Libf8b1da2020-04-15 16:32:50 +080069 auto p = std::make_shared<PSUSubEvent>(
Yong Li3046a022020-04-03 13:01:02 +080070 eventInterface, path, conn, io, eventName, eventName, assert,
Yong Libf8b1da2020-04-15 16:32:50 +080071 combineEvent, state, psuName);
72 p->setupRead();
73
74 events[eventPSUName].emplace_back(p);
Cheng C Yang58b2b532019-05-31 00:19:45 +080075 asserts.emplace_back(assert);
76 states.emplace_back(state);
77 }
78 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +080079
80 for (const auto& groupPathList : groupEventPathList)
81 {
82 for (const auto& pathList : groupPathList.second)
83 {
84 std::shared_ptr<std::set<std::string>> assert =
85 std::make_shared<std::set<std::string>>();
86 std::shared_ptr<bool> state = std::make_shared<bool>(false);
87
88 const std::string& groupEventName = pathList.first;
89 std::string eventPSUName = groupEventName + psuName;
90 for (const auto& path : pathList.second)
91 {
Yong Libf8b1da2020-04-15 16:32:50 +080092 auto p = std::make_shared<PSUSubEvent>(
Yong Li3046a022020-04-03 13:01:02 +080093 eventInterface, path, conn, io, groupEventName,
Yong Libf8b1da2020-04-15 16:32:50 +080094 groupPathList.first, assert, combineEvent, state, psuName);
95 p->setupRead();
96 events[eventPSUName].emplace_back(p);
97
Cheng C Yang202a1ff2020-01-09 09:34:22 +080098 asserts.emplace_back(assert);
99 states.emplace_back(state);
100 }
101 }
102 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800103}
104
105PSUCombineEvent::~PSUCombineEvent()
106{
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800107 // Clear unique_ptr first
108 for (auto& event : events)
109 {
110 for (auto& subEventPtr : event.second)
111 {
112 subEventPtr.reset();
113 }
114 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800115 events.clear();
116 objServer.remove_interface(eventInterface);
117}
118
Cheng C Yang9b53a622019-08-27 22:13:58 +0800119static boost::container::flat_map<std::string,
120 std::pair<std::string, std::string>>
121 logID = {
122 {"PredictiveFailure",
123 {"OpenBMC.0.1.PowerSupplyFailurePredicted",
124 "OpenBMC.0.1.PowerSupplyPredictedFailureRecovered"}},
125 {"Failure",
126 {"OpenBMC.0.1.PowerSupplyFailed", "OpenBMC.0.1.PowerSupplyRecovered"}},
127 {"ACLost",
Cheng C Yangbb732ef2019-09-18 16:25:58 +0800128 {"OpenBMC.0.1.PowerSupplyPowerLost",
129 "OpenBMC.0.1.PowerSupplyPowerRestored"}},
Cheng C Yang9b53a622019-08-27 22:13:58 +0800130 {"FanFault",
131 {"OpenBMC.0.1.PowerSupplyFanFailed",
132 "OpenBMC.0.1.PowerSupplyFanRecovered"}},
jayaprakash Mutyalad12f23e2019-12-03 23:03:52 +0000133 {"ConfigureError",
134 {"OpenBMC.0.1.PowerSupplyConfigurationError",
135 "OpenBMC.0.1.PowerSupplyConfigurationErrorRecovered"}}};
Cheng C Yang5665db22019-07-12 00:57:30 +0800136
Cheng C Yang58b2b532019-05-31 00:19:45 +0800137PSUSubEvent::PSUSubEvent(
138 std::shared_ptr<sdbusplus::asio::dbus_interface> eventInterface,
Yong Li3046a022020-04-03 13:01:02 +0800139 const std::string& path, std::shared_ptr<sdbusplus::asio::connection>& conn,
140 boost::asio::io_service& io, const std::string& groupEventName,
141 const std::string& eventName,
Cheng C Yang58b2b532019-05-31 00:19:45 +0800142 std::shared_ptr<std::set<std::string>> asserts,
143 std::shared_ptr<std::set<std::string>> combineEvent,
Cheng C Yang5665db22019-07-12 00:57:30 +0800144 std::shared_ptr<bool> state, const std::string& psuName) :
Yong Libf8b1da2020-04-15 16:32:50 +0800145 std::enable_shared_from_this<PSUSubEvent>(),
146 eventInterface(eventInterface), asserts(asserts),
147 combineEvent(combineEvent), assertState(state), errCount(0), path(path),
148 eventName(eventName), waitTimer(io), inputDev(io), psuName(psuName),
149 groupEventName(groupEventName), systemBus(conn)
Cheng C Yang58b2b532019-05-31 00:19:45 +0800150{
Cheng C Yanga97f1342020-02-11 15:10:41 +0800151 fd = open(path.c_str(), O_RDONLY);
152 if (fd < 0)
153 {
154 std::cerr << "PSU sub event failed to open file\n";
155 return;
156 }
157 inputDev.assign(fd);
158
Cheng C Yang5665db22019-07-12 00:57:30 +0800159 auto found = logID.find(eventName);
160 if (found == logID.end())
161 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800162 assertMessage.clear();
163 deassertMessage.clear();
Cheng C Yang5665db22019-07-12 00:57:30 +0800164 }
165 else
166 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800167 assertMessage = found->second.first;
168 deassertMessage = found->second.second;
Cheng C Yang5665db22019-07-12 00:57:30 +0800169 }
170
171 auto fanPos = path.find("fan");
172 if (fanPos != std::string::npos)
173 {
174 fanName = path.substr(fanPos);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800175 auto fanNamePos = fanName.find("_");
176 if (fanNamePos != std::string::npos)
177 {
178 fanName = fanName.substr(0, fanNamePos);
179 }
Cheng C Yang5665db22019-07-12 00:57:30 +0800180 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800181}
182
183void PSUSubEvent::setupRead(void)
184{
Yong Libf8b1da2020-04-15 16:32:50 +0800185 std::shared_ptr<boost::asio::streambuf> buffer =
186 std::make_shared<boost::asio::streambuf>();
187 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
188
Cheng C Yang58b2b532019-05-31 00:19:45 +0800189 boost::asio::async_read_until(
Yong Libf8b1da2020-04-15 16:32:50 +0800190 inputDev, *buffer, '\n',
191 [weakRef, buffer](const boost::system::error_code& ec,
192 std::size_t /*bytes_transfered*/) {
193 std::shared_ptr<PSUSubEvent> self = weakRef.lock();
194 if (self)
195 {
196 self->readBuf = buffer;
197 self->handleResponse(ec);
198 }
199 });
Cheng C Yang58b2b532019-05-31 00:19:45 +0800200}
201
202PSUSubEvent::~PSUSubEvent()
203{
Cheng C Yang58b2b532019-05-31 00:19:45 +0800204 waitTimer.cancel();
Cheng C Yanga97f1342020-02-11 15:10:41 +0800205 inputDev.close();
Cheng C Yang58b2b532019-05-31 00:19:45 +0800206}
207
208void PSUSubEvent::handleResponse(const boost::system::error_code& err)
209{
Yong Libf8b1da2020-04-15 16:32:50 +0800210 if ((err == boost::system::errc::bad_file_descriptor) ||
211 (err == boost::asio::error::misc_errors::not_found))
Cheng C Yang58b2b532019-05-31 00:19:45 +0800212 {
213 return;
214 }
Yong Libf8b1da2020-04-15 16:32:50 +0800215 std::istream responseStream(readBuf.get());
Cheng C Yang58b2b532019-05-31 00:19:45 +0800216 if (!err)
217 {
218 std::string response;
219 try
220 {
221 std::getline(responseStream, response);
Josh Lehan833661a2020-03-04 17:35:41 -0800222 int nvalue = std::stoi(response);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800223 responseStream.clear();
Josh Lehan833661a2020-03-04 17:35:41 -0800224
225 updateValue(nvalue);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800226 errCount = 0;
227 }
228 catch (const std::invalid_argument&)
229 {
230 errCount++;
231 }
232 }
233 else
234 {
235 errCount++;
236 }
237 if (errCount >= warnAfterErrorCount)
238 {
239 if (errCount == warnAfterErrorCount)
240 {
241 std::cerr << "Failure to read event at " << path << "\n";
242 }
243 updateValue(0);
244 errCount++;
245 }
Cheng C Yanga97f1342020-02-11 15:10:41 +0800246 lseek(fd, 0, SEEK_SET);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800247 waitTimer.expires_from_now(boost::posix_time::milliseconds(eventPollMs));
Yong Libf8b1da2020-04-15 16:32:50 +0800248
249 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
250 waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
251 std::shared_ptr<PSUSubEvent> self = weakRef.lock();
Cheng C Yang58b2b532019-05-31 00:19:45 +0800252 if (ec == boost::asio::error::operation_aborted)
253 {
254 return;
255 }
Yong Libf8b1da2020-04-15 16:32:50 +0800256 if (self)
257 {
258 self->setupRead();
259 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800260 });
261}
262
263// Any of the sub events of one event is asserted, then the event will be
264// asserted. Only if none of the sub events are asserted, the event will be
265// deasserted.
266void PSUSubEvent::updateValue(const int& newValue)
267{
Josh Lehan833661a2020-03-04 17:35:41 -0800268 // Take no action if value already equal
269 // Same semantics as Sensor::updateValue(const double&)
270 if (newValue == value)
271 {
272 return;
273 }
274
Cheng C Yang58b2b532019-05-31 00:19:45 +0800275 if (newValue == 0)
276 {
Yong Li591b1e42019-12-19 17:46:31 +0800277 // log deassert only after all asserts are gone
Cheng C Yang58b2b532019-05-31 00:19:45 +0800278 if (!(*asserts).empty())
279 {
Yong Li591b1e42019-12-19 17:46:31 +0800280 auto found = (*asserts).find(path);
281 if (found == (*asserts).end())
282 {
283 return;
284 }
285 (*asserts).erase(path);
286
Cheng C Yang58b2b532019-05-31 00:19:45 +0800287 return;
288 }
289 if (*assertState == true)
290 {
291 *assertState = false;
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800292 auto foundCombine = (*combineEvent).find(groupEventName);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800293 if (foundCombine == (*combineEvent).end())
Cheng C Yang58b2b532019-05-31 00:19:45 +0800294 {
295 return;
296 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800297 (*combineEvent).erase(groupEventName);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800298 if (!deassertMessage.empty())
299 {
300 // Fan Failed has two args
301 std::string sendMessage = eventName + " deassert";
302 if (deassertMessage == "OpenBMC.0.1.PowerSupplyFanRecovered")
303 {
304 sd_journal_send(
305 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800306 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
Cheng C Yang9b53a622019-08-27 22:13:58 +0800307 deassertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s,%s",
308 psuName.c_str(), fanName.c_str(), NULL);
309 }
310 else
311 {
312 sd_journal_send(
313 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800314 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
Cheng C Yang9b53a622019-08-27 22:13:58 +0800315 deassertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s",
316 psuName.c_str(), NULL);
317 }
318 }
319
Cheng C Yang58b2b532019-05-31 00:19:45 +0800320 if ((*combineEvent).empty())
321 {
322 eventInterface->set_property("functional", true);
323 }
324 }
325 }
326 else
327 {
Yong Li591b1e42019-12-19 17:46:31 +0800328 if ((*assertState == false) && ((*asserts).empty()))
Cheng C Yang58b2b532019-05-31 00:19:45 +0800329 {
330 *assertState = true;
Cheng C Yang9b53a622019-08-27 22:13:58 +0800331 if (!assertMessage.empty())
Cheng C Yang5665db22019-07-12 00:57:30 +0800332 {
Yong Li3046a022020-04-03 13:01:02 +0800333 // For failure and configure error, spec requires a beep
334 if ((assertMessage == "OpenBMC.0.1.PowerSupplyFailed") ||
335 (assertMessage ==
336 "OpenBMC.0.1.PowerSupplyConfigurationError"))
337 {
338 std::cout << " beep for " << assertMessage << "\n";
339 beep(beepPSUFailure);
340 }
341
Cheng C Yang5665db22019-07-12 00:57:30 +0800342 // Fan Failed has two args
343 std::string sendMessage = eventName + " assert";
Cheng C Yang9b53a622019-08-27 22:13:58 +0800344 if (assertMessage == "OpenBMC.0.1.PowerSupplyFanFailed")
Cheng C Yang5665db22019-07-12 00:57:30 +0800345 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800346 sd_journal_send(
347 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800348 LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
349 assertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s,%s",
350 psuName.c_str(), fanName.c_str(), NULL);
Cheng C Yang5665db22019-07-12 00:57:30 +0800351 }
352 else
353 {
354 sd_journal_send(
355 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800356 LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
357 assertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s",
358 psuName.c_str(), NULL);
Cheng C Yang5665db22019-07-12 00:57:30 +0800359 }
360 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800361 if ((*combineEvent).empty())
362 {
363 eventInterface->set_property("functional", false);
364 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800365 (*combineEvent).emplace(groupEventName);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800366 }
Yong Li591b1e42019-12-19 17:46:31 +0800367 (*asserts).emplace(path);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800368 }
369 value = newValue;
370}
Yong Li3046a022020-04-03 13:01:02 +0800371
372void PSUSubEvent::beep(const uint8_t& beepPriority)
373{
374 systemBus->async_method_call(
375 [](boost::system::error_code ec) {
376 if (ec)
377 {
378 std::cerr << "beep error (ec = " << ec << ")\n";
379 return;
380 }
381 },
382 "xyz.openbmc_project.BeepCode", "/xyz/openbmc_project/BeepCode",
383 "xyz.openbmc_project.BeepCode", "Beep", uint8_t(beepPriority));
384}