blob: 9e73df5fcbdeddf524f0c36826214f7fcae194fd [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>
Ed Tanous6cb732a2021-02-18 15:33:51 -080020#include <SensorPaths.hpp>
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{
Ed Tanous6cb732a2021-02-18 15:33:51 -080048 std::string psuNameEscaped = sensor_paths::escapePathForDbus(psuName);
Cheng C Yang58b2b532019-05-31 00:19:45 +080049 eventInterface = objServer.add_interface(
Ed Tanous6cb732a2021-02-18 15:33:51 -080050 "/xyz/openbmc_project/State/Decorator/" + psuNameEscaped + "_" +
Cheng C Yang58b2b532019-05-31 00:19:45 +080051 combineEventName,
52 "xyz.openbmc_project.State.Decorator.OperationalStatus");
James Feistb6c0b912019-07-09 12:21:44 -070053 eventInterface->register_property("functional", true);
Cheng C Yang58b2b532019-05-31 00:19:45 +080054
55 if (!eventInterface->initialize())
56 {
57 std::cerr << "error initializing event interface\n";
58 }
59
60 std::shared_ptr<std::set<std::string>> combineEvent =
61 std::make_shared<std::set<std::string>>();
62 for (const auto& pathList : eventPathList)
63 {
Yong Li591b1e42019-12-19 17:46:31 +080064 std::shared_ptr<std::set<std::string>> assert =
65 std::make_shared<std::set<std::string>>();
Cheng C Yang202a1ff2020-01-09 09:34:22 +080066 std::shared_ptr<bool> state = std::make_shared<bool>(false);
Yong Li591b1e42019-12-19 17:46:31 +080067
Cheng C Yang58b2b532019-05-31 00:19:45 +080068 const std::string& eventName = pathList.first;
69 std::string eventPSUName = eventName + psuName;
70 for (const auto& path : pathList.second)
71 {
Yong Libf8b1da2020-04-15 16:32:50 +080072 auto p = std::make_shared<PSUSubEvent>(
Yong Li3046a022020-04-03 13:01:02 +080073 eventInterface, path, conn, io, eventName, eventName, assert,
Yong Libf8b1da2020-04-15 16:32:50 +080074 combineEvent, state, psuName);
75 p->setupRead();
76
77 events[eventPSUName].emplace_back(p);
Cheng C Yang58b2b532019-05-31 00:19:45 +080078 asserts.emplace_back(assert);
79 states.emplace_back(state);
80 }
81 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +080082
83 for (const auto& groupPathList : groupEventPathList)
84 {
85 for (const auto& pathList : groupPathList.second)
86 {
87 std::shared_ptr<std::set<std::string>> assert =
88 std::make_shared<std::set<std::string>>();
89 std::shared_ptr<bool> state = std::make_shared<bool>(false);
90
91 const std::string& groupEventName = pathList.first;
92 std::string eventPSUName = groupEventName + psuName;
93 for (const auto& path : pathList.second)
94 {
Yong Libf8b1da2020-04-15 16:32:50 +080095 auto p = std::make_shared<PSUSubEvent>(
Yong Li3046a022020-04-03 13:01:02 +080096 eventInterface, path, conn, io, groupEventName,
Yong Libf8b1da2020-04-15 16:32:50 +080097 groupPathList.first, assert, combineEvent, state, psuName);
98 p->setupRead();
99 events[eventPSUName].emplace_back(p);
100
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800101 asserts.emplace_back(assert);
102 states.emplace_back(state);
103 }
104 }
105 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800106}
107
108PSUCombineEvent::~PSUCombineEvent()
109{
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800110 // Clear unique_ptr first
111 for (auto& event : events)
112 {
113 for (auto& subEventPtr : event.second)
114 {
115 subEventPtr.reset();
116 }
117 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800118 events.clear();
119 objServer.remove_interface(eventInterface);
120}
121
Cheng C Yang9b53a622019-08-27 22:13:58 +0800122static boost::container::flat_map<std::string,
123 std::pair<std::string, std::string>>
124 logID = {
125 {"PredictiveFailure",
126 {"OpenBMC.0.1.PowerSupplyFailurePredicted",
127 "OpenBMC.0.1.PowerSupplyPredictedFailureRecovered"}},
128 {"Failure",
129 {"OpenBMC.0.1.PowerSupplyFailed", "OpenBMC.0.1.PowerSupplyRecovered"}},
130 {"ACLost",
Cheng C Yangbb732ef2019-09-18 16:25:58 +0800131 {"OpenBMC.0.1.PowerSupplyPowerLost",
132 "OpenBMC.0.1.PowerSupplyPowerRestored"}},
Cheng C Yang9b53a622019-08-27 22:13:58 +0800133 {"FanFault",
134 {"OpenBMC.0.1.PowerSupplyFanFailed",
135 "OpenBMC.0.1.PowerSupplyFanRecovered"}},
jayaprakash Mutyalad12f23e2019-12-03 23:03:52 +0000136 {"ConfigureError",
137 {"OpenBMC.0.1.PowerSupplyConfigurationError",
138 "OpenBMC.0.1.PowerSupplyConfigurationErrorRecovered"}}};
Cheng C Yang5665db22019-07-12 00:57:30 +0800139
Cheng C Yang58b2b532019-05-31 00:19:45 +0800140PSUSubEvent::PSUSubEvent(
141 std::shared_ptr<sdbusplus::asio::dbus_interface> eventInterface,
Yong Li3046a022020-04-03 13:01:02 +0800142 const std::string& path, std::shared_ptr<sdbusplus::asio::connection>& conn,
143 boost::asio::io_service& io, const std::string& groupEventName,
144 const std::string& eventName,
Cheng C Yang58b2b532019-05-31 00:19:45 +0800145 std::shared_ptr<std::set<std::string>> asserts,
146 std::shared_ptr<std::set<std::string>> combineEvent,
Cheng C Yang5665db22019-07-12 00:57:30 +0800147 std::shared_ptr<bool> state, const std::string& psuName) :
Yong Libf8b1da2020-04-15 16:32:50 +0800148 std::enable_shared_from_this<PSUSubEvent>(),
Ed Tanous8a57ec02020-10-09 12:46:52 -0700149 eventInterface(std::move(eventInterface)), asserts(std::move(asserts)),
150 combineEvent(std::move(combineEvent)), assertState(std::move(state)),
151 errCount(0), path(path), eventName(eventName), waitTimer(io), inputDev(io),
152 psuName(psuName), groupEventName(groupEventName), systemBus(conn)
Cheng C Yang58b2b532019-05-31 00:19:45 +0800153{
Cheng C Yanga97f1342020-02-11 15:10:41 +0800154 fd = open(path.c_str(), O_RDONLY);
155 if (fd < 0)
156 {
157 std::cerr << "PSU sub event failed to open file\n";
158 return;
159 }
160 inputDev.assign(fd);
161
Cheng C Yang5665db22019-07-12 00:57:30 +0800162 auto found = logID.find(eventName);
163 if (found == logID.end())
164 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800165 assertMessage.clear();
166 deassertMessage.clear();
Cheng C Yang5665db22019-07-12 00:57:30 +0800167 }
168 else
169 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800170 assertMessage = found->second.first;
171 deassertMessage = found->second.second;
Cheng C Yang5665db22019-07-12 00:57:30 +0800172 }
173
174 auto fanPos = path.find("fan");
175 if (fanPos != std::string::npos)
176 {
177 fanName = path.substr(fanPos);
Ed Tanous8a57ec02020-10-09 12:46:52 -0700178 auto fanNamePos = fanName.find('_');
Cheng C Yang9b53a622019-08-27 22:13:58 +0800179 if (fanNamePos != std::string::npos)
180 {
181 fanName = fanName.substr(0, fanNamePos);
182 }
Cheng C Yang5665db22019-07-12 00:57:30 +0800183 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800184}
185
186void PSUSubEvent::setupRead(void)
187{
Yong Libf8b1da2020-04-15 16:32:50 +0800188 std::shared_ptr<boost::asio::streambuf> buffer =
189 std::make_shared<boost::asio::streambuf>();
190 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
191
Cheng C Yang58b2b532019-05-31 00:19:45 +0800192 boost::asio::async_read_until(
Yong Libf8b1da2020-04-15 16:32:50 +0800193 inputDev, *buffer, '\n',
194 [weakRef, buffer](const boost::system::error_code& ec,
195 std::size_t /*bytes_transfered*/) {
196 std::shared_ptr<PSUSubEvent> self = weakRef.lock();
197 if (self)
198 {
199 self->readBuf = buffer;
200 self->handleResponse(ec);
201 }
202 });
Cheng C Yang58b2b532019-05-31 00:19:45 +0800203}
204
205PSUSubEvent::~PSUSubEvent()
206{
Cheng C Yang58b2b532019-05-31 00:19:45 +0800207 waitTimer.cancel();
Cheng C Yanga97f1342020-02-11 15:10:41 +0800208 inputDev.close();
Cheng C Yang58b2b532019-05-31 00:19:45 +0800209}
210
211void PSUSubEvent::handleResponse(const boost::system::error_code& err)
212{
Yong Libf8b1da2020-04-15 16:32:50 +0800213 if ((err == boost::system::errc::bad_file_descriptor) ||
214 (err == boost::asio::error::misc_errors::not_found))
Cheng C Yang58b2b532019-05-31 00:19:45 +0800215 {
216 return;
217 }
Yong Libf8b1da2020-04-15 16:32:50 +0800218 std::istream responseStream(readBuf.get());
Cheng C Yang58b2b532019-05-31 00:19:45 +0800219 if (!err)
220 {
221 std::string response;
222 try
223 {
224 std::getline(responseStream, response);
Josh Lehan833661a2020-03-04 17:35:41 -0800225 int nvalue = std::stoi(response);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800226 responseStream.clear();
Josh Lehan833661a2020-03-04 17:35:41 -0800227
228 updateValue(nvalue);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800229 errCount = 0;
230 }
231 catch (const std::invalid_argument&)
232 {
233 errCount++;
234 }
235 }
236 else
237 {
238 errCount++;
239 }
240 if (errCount >= warnAfterErrorCount)
241 {
242 if (errCount == warnAfterErrorCount)
243 {
244 std::cerr << "Failure to read event at " << path << "\n";
245 }
246 updateValue(0);
247 errCount++;
248 }
Cheng C Yanga97f1342020-02-11 15:10:41 +0800249 lseek(fd, 0, SEEK_SET);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800250 waitTimer.expires_from_now(boost::posix_time::milliseconds(eventPollMs));
Yong Libf8b1da2020-04-15 16:32:50 +0800251
252 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
253 waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
254 std::shared_ptr<PSUSubEvent> self = weakRef.lock();
Cheng C Yang58b2b532019-05-31 00:19:45 +0800255 if (ec == boost::asio::error::operation_aborted)
256 {
257 return;
258 }
Yong Libf8b1da2020-04-15 16:32:50 +0800259 if (self)
260 {
261 self->setupRead();
262 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800263 });
264}
265
266// Any of the sub events of one event is asserted, then the event will be
267// asserted. Only if none of the sub events are asserted, the event will be
268// deasserted.
269void PSUSubEvent::updateValue(const int& newValue)
270{
Josh Lehan833661a2020-03-04 17:35:41 -0800271 // Take no action if value already equal
272 // Same semantics as Sensor::updateValue(const double&)
273 if (newValue == value)
274 {
275 return;
276 }
277
Cheng C Yang58b2b532019-05-31 00:19:45 +0800278 if (newValue == 0)
279 {
Yong Li591b1e42019-12-19 17:46:31 +0800280 // log deassert only after all asserts are gone
Cheng C Yang58b2b532019-05-31 00:19:45 +0800281 if (!(*asserts).empty())
282 {
Yong Li591b1e42019-12-19 17:46:31 +0800283 auto found = (*asserts).find(path);
284 if (found == (*asserts).end())
285 {
286 return;
287 }
288 (*asserts).erase(path);
289
Cheng C Yang58b2b532019-05-31 00:19:45 +0800290 return;
291 }
292 if (*assertState == true)
293 {
294 *assertState = false;
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800295 auto foundCombine = (*combineEvent).find(groupEventName);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800296 if (foundCombine == (*combineEvent).end())
Cheng C Yang58b2b532019-05-31 00:19:45 +0800297 {
298 return;
299 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800300 (*combineEvent).erase(groupEventName);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800301 if (!deassertMessage.empty())
302 {
303 // Fan Failed has two args
304 std::string sendMessage = eventName + " deassert";
305 if (deassertMessage == "OpenBMC.0.1.PowerSupplyFanRecovered")
306 {
307 sd_journal_send(
308 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800309 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
Cheng C Yang9b53a622019-08-27 22:13:58 +0800310 deassertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s,%s",
311 psuName.c_str(), fanName.c_str(), NULL);
312 }
313 else
314 {
315 sd_journal_send(
316 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800317 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
Cheng C Yang9b53a622019-08-27 22:13:58 +0800318 deassertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s",
319 psuName.c_str(), NULL);
320 }
321 }
322
Cheng C Yang58b2b532019-05-31 00:19:45 +0800323 if ((*combineEvent).empty())
324 {
325 eventInterface->set_property("functional", true);
326 }
327 }
328 }
329 else
330 {
Yong Li591b1e42019-12-19 17:46:31 +0800331 if ((*assertState == false) && ((*asserts).empty()))
Cheng C Yang58b2b532019-05-31 00:19:45 +0800332 {
333 *assertState = true;
Cheng C Yang9b53a622019-08-27 22:13:58 +0800334 if (!assertMessage.empty())
Cheng C Yang5665db22019-07-12 00:57:30 +0800335 {
Yong Li3046a022020-04-03 13:01:02 +0800336 // For failure and configure error, spec requires a beep
337 if ((assertMessage == "OpenBMC.0.1.PowerSupplyFailed") ||
338 (assertMessage ==
339 "OpenBMC.0.1.PowerSupplyConfigurationError"))
340 {
341 std::cout << " beep for " << assertMessage << "\n";
342 beep(beepPSUFailure);
343 }
344
Cheng C Yang5665db22019-07-12 00:57:30 +0800345 // Fan Failed has two args
346 std::string sendMessage = eventName + " assert";
Cheng C Yang9b53a622019-08-27 22:13:58 +0800347 if (assertMessage == "OpenBMC.0.1.PowerSupplyFanFailed")
Cheng C Yang5665db22019-07-12 00:57:30 +0800348 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800349 sd_journal_send(
350 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800351 LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
352 assertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s,%s",
353 psuName.c_str(), fanName.c_str(), NULL);
Cheng C Yang5665db22019-07-12 00:57:30 +0800354 }
355 else
356 {
357 sd_journal_send(
358 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800359 LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
360 assertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s",
361 psuName.c_str(), NULL);
Cheng C Yang5665db22019-07-12 00:57:30 +0800362 }
363 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800364 if ((*combineEvent).empty())
365 {
366 eventInterface->set_property("functional", false);
367 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800368 (*combineEvent).emplace(groupEventName);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800369 }
Yong Li591b1e42019-12-19 17:46:31 +0800370 (*asserts).emplace(path);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800371 }
372 value = newValue;
373}
Yong Li3046a022020-04-03 13:01:02 +0800374
375void PSUSubEvent::beep(const uint8_t& beepPriority)
376{
377 systemBus->async_method_call(
378 [](boost::system::error_code ec) {
379 if (ec)
380 {
381 std::cerr << "beep error (ec = " << ec << ")\n";
382 return;
383 }
384 },
385 "xyz.openbmc_project.BeepCode", "/xyz/openbmc_project/BeepCode",
386 "xyz.openbmc_project.BeepCode", "Beep", uint8_t(beepPriority));
387}