blob: da63e0d92807cc670cf3baf7cec15128ce4bd79b [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>
Zhikui Ren672bdfc2020-07-14 11:37:01 -070020
Jason M. Bills2b9704d2018-11-02 13:12:09 -070021#include <string_view>
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -070022#include <variant>
Jason M. Bills5e049d32018-10-19 12:59:38 -070023
24enum class thresholdEventOffsets : uint8_t
25{
26 lowerNonCritGoingLow = 0x00,
27 lowerCritGoingLow = 0x02,
28 upperNonCritGoingHigh = 0x07,
29 upperCritGoingHigh = 0x09,
30};
31
32static constexpr const uint8_t thresholdEventDataTriggerReadingByte2 = (1 << 6);
33static constexpr const uint8_t thresholdEventDataTriggerReadingByte3 = (1 << 4);
34
Jason M. Bills60ec28a2019-04-11 16:53:44 -070035static const std::string openBMCMessageRegistryVersion("0.1");
Jason M. Bills2b9704d2018-11-02 13:12:09 -070036
Jason M. Bills5e049d32018-10-19 12:59:38 -070037inline static sdbusplus::bus::match::match startThresholdEventMonitor(
38 std::shared_ptr<sdbusplus::asio::connection> conn)
39{
40 auto thresholdEventMatcherCallback = [conn](
Zhikui Ren672bdfc2020-07-14 11:37:01 -070041 sdbusplus::message::message& msg) {
Jason M. Bills5e049d32018-10-19 12:59:38 -070042 // This static set of std::pair<path, event> tracks asserted events to
43 // avoid duplicate logs or deasserts logged without an assert
44 static boost::container::flat_set<std::pair<std::string, std::string>>
45 assertedEvents;
46 // SEL event data is three bytes where 0xFF means unspecified
47 std::vector<uint8_t> eventData(selEvtDataMaxSize, 0xFF);
48
49 // Get the event type and assertion details from the message
50 std::string thresholdInterface;
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -070051 boost::container::flat_map<std::string, std::variant<bool>>
Jason M. Bills5e049d32018-10-19 12:59:38 -070052 propertiesChanged;
53 msg.read(thresholdInterface, propertiesChanged);
54 std::string event = propertiesChanged.begin()->first;
Zhikui Ren672bdfc2020-07-14 11:37:01 -070055 bool* pval = std::get_if<bool>(&propertiesChanged.begin()->second);
Jason M. Bills5e049d32018-10-19 12:59:38 -070056 if (!pval)
57 {
58 std::cerr << "threshold event direction has invalid type\n";
59 return;
60 }
61 bool assert = *pval;
62
63 // Check the asserted events to determine if we should log this event
64 std::pair<std::string, std::string> pathAndEvent(
65 std::string(msg.get_path()), event);
66 if (assert)
67 {
68 // For asserts, add the event to the set and only log it if it's new
69 if (assertedEvents.insert(pathAndEvent).second == false)
70 {
71 // event is already in the set
72 return;
73 }
74 }
75 else
76 {
77 // For deasserts, remove the event and only log the deassert if it
78 // was asserted
79 if (assertedEvents.erase(pathAndEvent) == 0)
80 {
81 // asserted event was not in the set
82 return;
83 }
84 }
85
86 // Set the IPMI threshold event type based on the event details from the
87 // message
88 if (event == "CriticalAlarmLow")
89 {
90 eventData[0] =
91 static_cast<uint8_t>(thresholdEventOffsets::lowerCritGoingLow);
92 }
93 else if (event == "WarningAlarmLow")
94 {
95 eventData[0] = static_cast<uint8_t>(
96 thresholdEventOffsets::lowerNonCritGoingLow);
97 }
98 else if (event == "WarningAlarmHigh")
99 {
100 eventData[0] = static_cast<uint8_t>(
101 thresholdEventOffsets::upperNonCritGoingHigh);
102 }
103 else if (event == "CriticalAlarmHigh")
104 {
105 eventData[0] =
106 static_cast<uint8_t>(thresholdEventOffsets::upperCritGoingHigh);
107 }
108 // Indicate that bytes 2 and 3 are threshold sensor trigger values
109 eventData[0] |= thresholdEventDataTriggerReadingByte2 |
110 thresholdEventDataTriggerReadingByte3;
111
112 // Get the sensor reading to put in the event data
113 sdbusplus::message::message getSensorValue =
114 conn->new_method_call(msg.get_sender(), msg.get_path(),
115 "org.freedesktop.DBus.Properties", "GetAll");
116 getSensorValue.append("xyz.openbmc_project.Sensor.Value");
George Hungd9d78962019-03-27 13:25:24 +0800117 boost::container::flat_map<std::string, std::variant<double, int64_t>>
Jason M. Bills5e049d32018-10-19 12:59:38 -0700118 sensorValue;
119 try
120 {
121 sdbusplus::message::message getSensorValueResp =
122 conn->call(getSensorValue);
123 getSensorValueResp.read(sensorValue);
124 }
Zhikui Ren672bdfc2020-07-14 11:37:01 -0700125 catch (sdbusplus::exception_t&)
Jason M. Bills5e049d32018-10-19 12:59:38 -0700126 {
127 std::cerr << "error getting sensor value from " << msg.get_path()
128 << "\n";
129 return;
130 }
Jason M. Billsbb071fb2019-03-27 11:15:42 -0700131 double max = 0;
132 auto findMax = sensorValue.find("MaxValue");
133 if (findMax != sensorValue.end())
134 {
135 max = std::visit(ipmi::VariantToDoubleVisitor(), findMax->second);
136 }
137 double min = 0;
138 auto findMin = sensorValue.find("MinValue");
139 if (findMin != sensorValue.end())
140 {
141 min = std::visit(ipmi::VariantToDoubleVisitor(), findMin->second);
142 }
143 double sensorVal = 0;
144 auto findVal = sensorValue.find("Value");
145 if (findVal != sensorValue.end())
146 {
147 sensorVal =
148 std::visit(ipmi::VariantToDoubleVisitor(), findVal->second);
149 }
George Hungd9d78962019-03-27 13:25:24 +0800150 double scale = 0;
151 auto findScale = sensorValue.find("Scale");
152 if (findScale != sensorValue.end())
153 {
154 scale =
155 std::visit(ipmi::VariantToDoubleVisitor(), findScale->second);
156
157 sensorVal *= std::pow(10, scale);
158 }
Jason M. Bills5e049d32018-10-19 12:59:38 -0700159 try
160 {
161 eventData[1] = ipmi::getScaledIPMIValue(sensorVal, max, min);
162 }
Zhikui Ren672bdfc2020-07-14 11:37:01 -0700163 catch (const std::exception& e)
Jason M. Bills5e049d32018-10-19 12:59:38 -0700164 {
165 std::cerr << e.what();
166 eventData[1] = 0xFF;
167 }
168
169 // Get the threshold value to put in the event data
170 // Get the threshold parameter by removing the "Alarm" text from the
171 // event string
172 std::string alarm("Alarm");
173 if (std::string::size_type pos = event.find(alarm);
174 pos != std::string::npos)
175 {
176 event.erase(pos, alarm.length());
177 }
178 sdbusplus::message::message getThreshold =
179 conn->new_method_call(msg.get_sender(), msg.get_path(),
180 "org.freedesktop.DBus.Properties", "Get");
181 getThreshold.append(thresholdInterface, event);
George Hungd9d78962019-03-27 13:25:24 +0800182 std::variant<double, int64_t> thresholdValue;
Jason M. Bills5e049d32018-10-19 12:59:38 -0700183 try
184 {
185 sdbusplus::message::message getThresholdResp =
186 conn->call(getThreshold);
187 getThresholdResp.read(thresholdValue);
188 }
Zhikui Ren672bdfc2020-07-14 11:37:01 -0700189 catch (sdbusplus::exception_t&)
Jason M. Bills5e049d32018-10-19 12:59:38 -0700190 {
191 std::cerr << "error getting sensor threshold from "
192 << msg.get_path() << "\n";
193 return;
194 }
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -0700195 double thresholdVal =
196 std::visit(ipmi::VariantToDoubleVisitor(), thresholdValue);
George Hungd9d78962019-03-27 13:25:24 +0800197 if (findScale != sensorValue.end())
198 {
199 thresholdVal *= std::pow(10, scale);
200 }
Jason M. Bills5e049d32018-10-19 12:59:38 -0700201 try
202 {
203 eventData[2] = ipmi::getScaledIPMIValue(thresholdVal, max, min);
204 }
Zhikui Ren672bdfc2020-07-14 11:37:01 -0700205 catch (const std::exception& e)
Jason M. Bills5e049d32018-10-19 12:59:38 -0700206 {
207 std::cerr << e.what();
208 eventData[2] = 0xFF;
209 }
210
211 // Construct a human-readable message of this event for the log
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700212 std::string_view sensorName(msg.get_path());
Jason M. Bills5e049d32018-10-19 12:59:38 -0700213 sensorName.remove_prefix(
214 std::min(sensorName.find_last_of("/") + 1, sensorName.size()));
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700215
216 std::string threshold;
217 std::string direction;
Jason M. Billsf2552a52019-03-28 11:54:48 -0700218 std::string redfishMessageID =
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700219 "OpenBMC." + openBMCMessageRegistryVersion;
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700220 if (event == "CriticalLow")
221 {
222 threshold = "critical low";
223 if (assert)
224 {
225 direction = "low";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700226 redfishMessageID += ".SensorThresholdCriticalLowGoingLow";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700227 }
228 else
229 {
230 direction = "high";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700231 redfishMessageID += ".SensorThresholdCriticalLowGoingHigh";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700232 }
233 }
234 else if (event == "WarningLow")
235 {
236 threshold = "warning low";
237 if (assert)
238 {
239 direction = "low";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700240 redfishMessageID += ".SensorThresholdWarningLowGoingLow";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700241 }
242 else
243 {
244 direction = "high";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700245 redfishMessageID += ".SensorThresholdWarningLowGoingHigh";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700246 }
247 }
248 else if (event == "WarningHigh")
249 {
250 threshold = "warning high";
251 if (assert)
252 {
253 direction = "high";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700254 redfishMessageID += ".SensorThresholdWarningHighGoingHigh";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700255 }
256 else
257 {
258 direction = "low";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700259 redfishMessageID += ".SensorThresholdWarningHighGoingLow";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700260 }
261 }
262 else if (event == "CriticalHigh")
263 {
264 threshold = "critical high";
265 if (assert)
266 {
267 direction = "high";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700268 redfishMessageID += ".SensorThresholdCriticalHighGoingHigh";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700269 }
270 else
271 {
272 direction = "low";
Jason M. Bills60ec28a2019-04-11 16:53:44 -0700273 redfishMessageID += ".SensorThresholdCriticalHighGoingLow";
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700274 }
275 }
276
277 std::string journalMsg(std::string(sensorName) + " sensor crossed a " +
278 threshold + " threshold going " + direction +
Jason M. Bills5e049d32018-10-19 12:59:38 -0700279 ". Reading=" + std::to_string(sensorVal) +
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700280 " Threshold=" + std::to_string(thresholdVal) +
281 ".");
Jason M. Bills5e049d32018-10-19 12:59:38 -0700282
Jason M. Bills5ff505f2019-04-11 16:58:01 -0700283 selAddSystemRecord(
284 journalMsg, std::string(msg.get_path()), eventData, assert,
285 selBMCGenID, "REDFISH_MESSAGE_ID=%s", redfishMessageID.c_str(),
286 "REDFISH_MESSAGE_ARGS=%.*s,%f,%f", sensorName.length(),
287 sensorName.data(), sensorVal, thresholdVal);
Jason M. Bills5e049d32018-10-19 12:59:38 -0700288 };
289 sdbusplus::bus::match::match thresholdEventMatcher(
Zhikui Ren672bdfc2020-07-14 11:37:01 -0700290 static_cast<sdbusplus::bus::bus&>(*conn),
Jason M. Bills5e049d32018-10-19 12:59:38 -0700291 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
292 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Sensor."
293 "Threshold'",
294 std::move(thresholdEventMatcherCallback));
295 return thresholdEventMatcher;
296}