blob: b2473ef8e976e22fa3cd1010152cde31b005f37d [file] [log] [blame]
Jason M. Bills5e049d32018-10-19 12:59:38 -07001/*
2// Copyright (c) 2018 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
17#pragma once
18#include <sel_logger.hpp>
19#include <sensorutils.hpp>
Jason M. Bills2b9704d2018-11-02 13:12:09 -070020#include <string_view>
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -070021#include <variant>
Jason M. Bills5e049d32018-10-19 12:59:38 -070022
23enum class thresholdEventOffsets : uint8_t
24{
25 lowerNonCritGoingLow = 0x00,
26 lowerCritGoingLow = 0x02,
27 upperNonCritGoingHigh = 0x07,
28 upperCritGoingHigh = 0x09,
29};
30
31static constexpr const uint8_t thresholdEventDataTriggerReadingByte2 = (1 << 6);
32static constexpr const uint8_t thresholdEventDataTriggerReadingByte3 = (1 << 4);
33
Jason M. Bills2b9704d2018-11-02 13:12:09 -070034static const std::string openBMCMessageRegistryVersion("0.1");
35
Jason M. Bills5e049d32018-10-19 12:59:38 -070036inline static sdbusplus::bus::match::match startThresholdEventMonitor(
37 std::shared_ptr<sdbusplus::asio::connection> conn)
38{
39 auto thresholdEventMatcherCallback = [conn](
40 sdbusplus::message::message &msg) {
41 // This static set of std::pair<path, event> tracks asserted events to
42 // avoid duplicate logs or deasserts logged without an assert
43 static boost::container::flat_set<std::pair<std::string, std::string>>
44 assertedEvents;
45 // SEL event data is three bytes where 0xFF means unspecified
46 std::vector<uint8_t> eventData(selEvtDataMaxSize, 0xFF);
47
48 // Get the event type and assertion details from the message
49 std::string thresholdInterface;
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -070050 boost::container::flat_map<std::string, std::variant<bool>>
Jason M. Bills5e049d32018-10-19 12:59:38 -070051 propertiesChanged;
52 msg.read(thresholdInterface, propertiesChanged);
53 std::string event = propertiesChanged.begin()->first;
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -070054 bool *pval = std::get_if<bool>(&propertiesChanged.begin()->second);
Jason M. Bills5e049d32018-10-19 12:59:38 -070055 if (!pval)
56 {
57 std::cerr << "threshold event direction has invalid type\n";
58 return;
59 }
60 bool assert = *pval;
61
62 // Check the asserted events to determine if we should log this event
63 std::pair<std::string, std::string> pathAndEvent(
64 std::string(msg.get_path()), event);
65 if (assert)
66 {
67 // For asserts, add the event to the set and only log it if it's new
68 if (assertedEvents.insert(pathAndEvent).second == false)
69 {
70 // event is already in the set
71 return;
72 }
73 }
74 else
75 {
76 // For deasserts, remove the event and only log the deassert if it
77 // was asserted
78 if (assertedEvents.erase(pathAndEvent) == 0)
79 {
80 // asserted event was not in the set
81 return;
82 }
83 }
84
85 // Set the IPMI threshold event type based on the event details from the
86 // message
87 if (event == "CriticalAlarmLow")
88 {
89 eventData[0] =
90 static_cast<uint8_t>(thresholdEventOffsets::lowerCritGoingLow);
91 }
92 else if (event == "WarningAlarmLow")
93 {
94 eventData[0] = static_cast<uint8_t>(
95 thresholdEventOffsets::lowerNonCritGoingLow);
96 }
97 else if (event == "WarningAlarmHigh")
98 {
99 eventData[0] = static_cast<uint8_t>(
100 thresholdEventOffsets::upperNonCritGoingHigh);
101 }
102 else if (event == "CriticalAlarmHigh")
103 {
104 eventData[0] =
105 static_cast<uint8_t>(thresholdEventOffsets::upperCritGoingHigh);
106 }
107 // Indicate that bytes 2 and 3 are threshold sensor trigger values
108 eventData[0] |= thresholdEventDataTriggerReadingByte2 |
109 thresholdEventDataTriggerReadingByte3;
110
111 // Get the sensor reading to put in the event data
112 sdbusplus::message::message getSensorValue =
113 conn->new_method_call(msg.get_sender(), msg.get_path(),
114 "org.freedesktop.DBus.Properties", "GetAll");
115 getSensorValue.append("xyz.openbmc_project.Sensor.Value");
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -0700116 boost::container::flat_map<std::string, std::variant<double>>
Jason M. Bills5e049d32018-10-19 12:59:38 -0700117 sensorValue;
118 try
119 {
120 sdbusplus::message::message getSensorValueResp =
121 conn->call(getSensorValue);
122 getSensorValueResp.read(sensorValue);
123 }
124 catch (sdbusplus::exception_t &)
125 {
126 std::cerr << "error getting sensor value from " << msg.get_path()
127 << "\n";
128 return;
129 }
Jason M. Billsbb071fb2019-03-27 11:15:42 -0700130 double max = 0;
131 auto findMax = sensorValue.find("MaxValue");
132 if (findMax != sensorValue.end())
133 {
134 max = std::visit(ipmi::VariantToDoubleVisitor(), findMax->second);
135 }
136 double min = 0;
137 auto findMin = sensorValue.find("MinValue");
138 if (findMin != sensorValue.end())
139 {
140 min = std::visit(ipmi::VariantToDoubleVisitor(), findMin->second);
141 }
142 double sensorVal = 0;
143 auto findVal = sensorValue.find("Value");
144 if (findVal != sensorValue.end())
145 {
146 sensorVal =
147 std::visit(ipmi::VariantToDoubleVisitor(), findVal->second);
148 }
Jason M. Bills5e049d32018-10-19 12:59:38 -0700149 try
150 {
151 eventData[1] = ipmi::getScaledIPMIValue(sensorVal, max, min);
152 }
153 catch (std::runtime_error &e)
154 {
155 std::cerr << e.what();
156 eventData[1] = 0xFF;
157 }
158
159 // Get the threshold value to put in the event data
160 // Get the threshold parameter by removing the "Alarm" text from the
161 // event string
162 std::string alarm("Alarm");
163 if (std::string::size_type pos = event.find(alarm);
164 pos != std::string::npos)
165 {
166 event.erase(pos, alarm.length());
167 }
168 sdbusplus::message::message getThreshold =
169 conn->new_method_call(msg.get_sender(), msg.get_path(),
170 "org.freedesktop.DBus.Properties", "Get");
171 getThreshold.append(thresholdInterface, event);
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -0700172 std::variant<double> thresholdValue;
Jason M. Bills5e049d32018-10-19 12:59:38 -0700173 try
174 {
175 sdbusplus::message::message getThresholdResp =
176 conn->call(getThreshold);
177 getThresholdResp.read(thresholdValue);
178 }
179 catch (sdbusplus::exception_t &)
180 {
181 std::cerr << "error getting sensor threshold from "
182 << msg.get_path() << "\n";
183 return;
184 }
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -0700185 double thresholdVal =
186 std::visit(ipmi::VariantToDoubleVisitor(), thresholdValue);
Jason M. Bills5e049d32018-10-19 12:59:38 -0700187 try
188 {
189 eventData[2] = ipmi::getScaledIPMIValue(thresholdVal, max, min);
190 }
191 catch (std::runtime_error &e)
192 {
193 std::cerr << e.what();
194 eventData[2] = 0xFF;
195 }
196
197 // Construct a human-readable message of this event for the log
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700198 std::string_view sensorName(msg.get_path());
Jason M. Bills5e049d32018-10-19 12:59:38 -0700199 sensorName.remove_prefix(
200 std::min(sensorName.find_last_of("/") + 1, sensorName.size()));
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700201
202 std::string threshold;
203 std::string direction;
204 std::string redfishMessageID;
205 if (event == "CriticalLow")
206 {
207 threshold = "critical low";
208 if (assert)
209 {
210 direction = "low";
211 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
212 ".SensorThresholdCriticalLowGoingLow";
213 }
214 else
215 {
216 direction = "high";
217 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
218 ".SensorThresholdCriticalLowGoingHigh";
219 }
220 }
221 else if (event == "WarningLow")
222 {
223 threshold = "warning low";
224 if (assert)
225 {
226 direction = "low";
227 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
228 ".SensorThresholdWarningLowGoingLow";
229 }
230 else
231 {
232 direction = "high";
233 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
234 ".SensorThresholdWarningLowGoingHigh";
235 }
236 }
237 else if (event == "WarningHigh")
238 {
239 threshold = "warning high";
240 if (assert)
241 {
242 direction = "high";
243 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
244 ".SensorThresholdWarningHighGoingHigh";
245 }
246 else
247 {
248 direction = "low";
249 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
250 ".SensorThresholdWarningHighGoingLow";
251 }
252 }
253 else if (event == "CriticalHigh")
254 {
255 threshold = "critical high";
256 if (assert)
257 {
258 direction = "high";
259 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
260 ".SensorThresholdCriticalHighGoingHigh";
261 }
262 else
263 {
264 direction = "low";
265 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
266 ".SensorThresholdCriticalHighGoingLow";
267 }
268 }
269
270 std::string journalMsg(std::string(sensorName) + " sensor crossed a " +
271 threshold + " threshold going " + direction +
Jason M. Bills5e049d32018-10-19 12:59:38 -0700272 ". Reading=" + std::to_string(sensorVal) +
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700273 " Threshold=" + std::to_string(thresholdVal) +
274 ".");
Jason M. Bills5e049d32018-10-19 12:59:38 -0700275
276 selAddSystemRecord(journalMsg, std::string(msg.get_path()), eventData,
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700277 assert, selBMCGenID, "REDFISH_MESSAGE_ID=%.*s",
278 redfishMessageID.length(), redfishMessageID.data(),
279 "REDFISH_MESSAGE_ARG_1=%.*s", sensorName.length(),
280 sensorName.data(), "REDFISH_MESSAGE_ARG_2=%f",
281 sensorVal, "REDFISH_MESSAGE_ARG_3=%f", thresholdVal);
Jason M. Bills5e049d32018-10-19 12:59:38 -0700282 };
283 sdbusplus::bus::match::match thresholdEventMatcher(
284 static_cast<sdbusplus::bus::bus &>(*conn),
285 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
286 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Sensor."
287 "Threshold'",
288 std::move(thresholdEventMatcherCallback));
289 return thresholdEventMatcher;
290}