blob: 843d672a3ac56847e17820897578dd1b9e8402ba [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. Bills60ec28a2019-04-11 16:53:44 -070034static const std::string openBMCMessageRegistryVersion("0.1");
Jason M. Bills2b9704d2018-11-02 13:12:09 -070035
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");
George Hungd9d78962019-03-27 13:25:24 +0800116 boost::container::flat_map<std::string, std::variant<double, int64_t>>
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 }
George Hungd9d78962019-03-27 13:25:24 +0800149 double scale = 0;
150 auto findScale = sensorValue.find("Scale");
151 if (findScale != sensorValue.end())
152 {
153 scale =
154 std::visit(ipmi::VariantToDoubleVisitor(), findScale->second);
155
156 sensorVal *= std::pow(10, scale);
157 }
Jason M. Bills5e049d32018-10-19 12:59:38 -0700158 try
159 {
160 eventData[1] = ipmi::getScaledIPMIValue(sensorVal, max, min);
161 }
162 catch (std::runtime_error &e)
163 {
164 std::cerr << e.what();
165 eventData[1] = 0xFF;
166 }
167
168 // Get the threshold value to put in the event data
169 // Get the threshold parameter by removing the "Alarm" text from the
170 // event string
171 std::string alarm("Alarm");
172 if (std::string::size_type pos = event.find(alarm);
173 pos != std::string::npos)
174 {
175 event.erase(pos, alarm.length());
176 }
177 sdbusplus::message::message getThreshold =
178 conn->new_method_call(msg.get_sender(), msg.get_path(),
179 "org.freedesktop.DBus.Properties", "Get");
180 getThreshold.append(thresholdInterface, event);
George Hungd9d78962019-03-27 13:25:24 +0800181 std::variant<double, int64_t> thresholdValue;
Jason M. Bills5e049d32018-10-19 12:59:38 -0700182 try
183 {
184 sdbusplus::message::message getThresholdResp =
185 conn->call(getThreshold);
186 getThresholdResp.read(thresholdValue);
187 }
188 catch (sdbusplus::exception_t &)
189 {
190 std::cerr << "error getting sensor threshold from "
191 << msg.get_path() << "\n";
192 return;
193 }
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -0700194 double thresholdVal =
195 std::visit(ipmi::VariantToDoubleVisitor(), thresholdValue);
George Hungd9d78962019-03-27 13:25:24 +0800196 if (findScale != sensorValue.end())
197 {
198 thresholdVal *= std::pow(10, scale);
199 }
Jason M. Bills5e049d32018-10-19 12:59:38 -0700200 try
201 {
202 eventData[2] = ipmi::getScaledIPMIValue(thresholdVal, max, min);
203 }
204 catch (std::runtime_error &e)
205 {
206 std::cerr << e.what();
207 eventData[2] = 0xFF;
208 }
209
210 // Construct a human-readable message of this event for the log
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700211 std::string_view sensorName(msg.get_path());
Jason M. Bills5e049d32018-10-19 12:59:38 -0700212 sensorName.remove_prefix(
213 std::min(sensorName.find_last_of("/") + 1, sensorName.size()));
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700214
215 std::string threshold;
216 std::string direction;
Jason M. Billsf2552a52019-03-28 11:54:48 -0700217 std::string redfishMessageID =
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700218 "OpenBMC." + openBMCMessageRegistryVersion;
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700219 if (event == "CriticalLow")
220 {
221 threshold = "critical low";
222 if (assert)
223 {
224 direction = "low";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700225 redfishMessageID += ".SensorThresholdCriticalLowGoingLow";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700226 }
227 else
228 {
229 direction = "high";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700230 redfishMessageID += ".SensorThresholdCriticalLowGoingHigh";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700231 }
232 }
233 else if (event == "WarningLow")
234 {
235 threshold = "warning low";
236 if (assert)
237 {
238 direction = "low";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700239 redfishMessageID += ".SensorThresholdWarningLowGoingLow";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700240 }
241 else
242 {
243 direction = "high";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700244 redfishMessageID += ".SensorThresholdWarningLowGoingHigh";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700245 }
246 }
247 else if (event == "WarningHigh")
248 {
249 threshold = "warning high";
250 if (assert)
251 {
252 direction = "high";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700253 redfishMessageID += ".SensorThresholdWarningHighGoingHigh";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700254 }
255 else
256 {
257 direction = "low";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700258 redfishMessageID += ".SensorThresholdWarningHighGoingLow";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700259 }
260 }
261 else if (event == "CriticalHigh")
262 {
263 threshold = "critical high";
264 if (assert)
265 {
266 direction = "high";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700267 redfishMessageID += ".SensorThresholdCriticalHighGoingHigh";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700268 }
269 else
270 {
271 direction = "low";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700272 redfishMessageID += ".SensorThresholdCriticalHighGoingLow";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700273 }
274 }
275
276 std::string journalMsg(std::string(sensorName) + " sensor crossed a " +
277 threshold + " threshold going " + direction +
Jason M. Bills5e049d32018-10-19 12:59:38 -0700278 ". Reading=" + std::to_string(sensorVal) +
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700279 " Threshold=" + std::to_string(thresholdVal) +
280 ".");
Jason M. Bills5e049d32018-10-19 12:59:38 -0700281
Jason M. Bills5ff505f2019-04-11 16:58:01 -0700282 selAddSystemRecord(
283 journalMsg, std::string(msg.get_path()), eventData, assert,
284 selBMCGenID, "REDFISH_MESSAGE_ID=%s", redfishMessageID.c_str(),
285 "REDFISH_MESSAGE_ARGS=%.*s,%f,%f", sensorName.length(),
286 sensorName.data(), sensorVal, thresholdVal);
Jason M. Bills5e049d32018-10-19 12:59:38 -0700287 };
288 sdbusplus::bus::match::match thresholdEventMatcher(
289 static_cast<sdbusplus::bus::bus &>(*conn),
290 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
291 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Sensor."
292 "Threshold'",
293 std::move(thresholdEventMatcherCallback));
294 return thresholdEventMatcher;
295}