blob: df56b2121c1f6a56acffc4b4ff1ea4605cb8030b [file] [log] [blame]
Ed Tanous8a57ec02020-10-09 12:46:52 -07001#include <Thresholds.hpp>
2#include <VariantVisitors.hpp>
James Feist6714a252018-09-10 15:26:18 -07003#include <boost/algorithm/string/replace.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -07004#include <boost/container/flat_map.hpp>
Ed Tanous8a57ec02020-10-09 12:46:52 -07005#include <sensor.hpp>
James Feist38fb5982020-05-28 10:09:54 -07006
7#include <array>
James Feistdc6c55f2018-10-31 12:53:20 -07008#include <cmath>
James Feist6714a252018-09-10 15:26:18 -07009#include <fstream>
10#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070011#include <memory>
12#include <stdexcept>
13#include <string>
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +020014#include <tuple>
Patrick Venture96e97db2019-10-31 13:44:38 -070015#include <utility>
16#include <variant>
17#include <vector>
James Feist6714a252018-09-10 15:26:18 -070018
Ed Tanous8a57ec02020-10-09 12:46:52 -070019static constexpr bool debug = false;
James Feist6714a252018-09-10 15:26:18 -070020namespace thresholds
21{
James Feistd8705872019-02-08 13:26:09 -080022unsigned int toBusValue(const Level& level)
James Feist6714a252018-09-10 15:26:18 -070023{
24 switch (level)
25 {
26 case (Level::WARNING):
27 {
28 return 0;
29 }
30 case (Level::CRITICAL):
31 {
32 return 1;
33 }
34 default:
35 {
36 return -1;
37 }
38 }
39}
40
James Feistd8705872019-02-08 13:26:09 -080041std::string toBusValue(const Direction& direction)
James Feist6714a252018-09-10 15:26:18 -070042{
43 switch (direction)
44 {
45 case (Direction::LOW):
46 {
47 return "less than";
48 }
49 case (Direction::HIGH):
50 {
51 return "greater than";
52 }
53 default:
54 {
55 return "err";
56 }
57 }
58}
59
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070060bool parseThresholdsFromConfig(
James Feistd8705872019-02-08 13:26:09 -080061 const SensorData& sensorData,
62 std::vector<thresholds::Threshold>& thresholdVector,
63 const std::string* matchLabel)
James Feist6714a252018-09-10 15:26:18 -070064{
James Feistd8705872019-02-08 13:26:09 -080065 for (const auto& item : sensorData)
James Feist6714a252018-09-10 15:26:18 -070066 {
67 if (item.first.find("Thresholds") == std::string::npos)
68 {
69 continue;
70 }
71 if (matchLabel != nullptr)
72 {
73 auto labelFind = item.second.find("Label");
74 if (labelFind == item.second.end())
Ed Tanous8a57ec02020-10-09 12:46:52 -070075 {
James Feist6714a252018-09-10 15:26:18 -070076 continue;
Ed Tanous8a57ec02020-10-09 12:46:52 -070077 }
James Feist3eb82622019-02-08 13:10:22 -080078 if (std::visit(VariantToStringVisitor(), labelFind->second) !=
79 *matchLabel)
Ed Tanous8a57ec02020-10-09 12:46:52 -070080 {
James Feist6714a252018-09-10 15:26:18 -070081 continue;
Ed Tanous8a57ec02020-10-09 12:46:52 -070082 }
James Feist6714a252018-09-10 15:26:18 -070083 }
84 auto directionFind = item.second.find("Direction");
85 auto severityFind = item.second.find("Severity");
86 auto valueFind = item.second.find("Value");
87 if (valueFind == item.second.end() ||
88 severityFind == item.second.end() ||
89 directionFind == item.second.end())
90 {
91 std::cerr << "Malformed threshold in configuration\n";
92 return false;
93 }
94 Level level;
95 Direction direction;
James Feist3eb82622019-02-08 13:10:22 -080096 if (std::visit(VariantToUnsignedIntVisitor(), severityFind->second) ==
97 0)
James Feist6714a252018-09-10 15:26:18 -070098 {
99 level = Level::WARNING;
100 }
101 else
102 {
103 level = Level::CRITICAL;
104 }
James Feist3eb82622019-02-08 13:10:22 -0800105 if (std::visit(VariantToStringVisitor(), directionFind->second) ==
106 "less than")
James Feist6714a252018-09-10 15:26:18 -0700107 {
108 direction = Direction::LOW;
109 }
110 else
111 {
112 direction = Direction::HIGH;
113 }
James Feist13f340b2019-03-07 16:36:11 -0800114 double val = std::visit(VariantToDoubleVisitor(), valueFind->second);
James Feist6714a252018-09-10 15:26:18 -0700115
116 thresholdVector.emplace_back(level, direction, val);
117 }
118 return true;
119}
120
James Feistd8705872019-02-08 13:26:09 -0800121void persistThreshold(const std::string& path, const std::string& baseInterface,
122 const thresholds::Threshold& threshold,
James Feista222ba72019-03-01 15:57:51 -0800123 std::shared_ptr<sdbusplus::asio::connection>& conn,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800124 size_t thresholdCount, const std::string& labelMatch)
James Feist6714a252018-09-10 15:26:18 -0700125{
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500126 for (size_t ii = 0; ii < thresholdCount; ii++)
James Feist6714a252018-09-10 15:26:18 -0700127 {
128 std::string thresholdInterface =
129 baseInterface + ".Thresholds" + std::to_string(ii);
130 conn->async_method_call(
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800131 [&, path, threshold, thresholdInterface, labelMatch](
James Feistd8705872019-02-08 13:26:09 -0800132 const boost::system::error_code& ec,
133 const boost::container::flat_map<std::string, BasicVariantType>&
134 result) {
James Feist6714a252018-09-10 15:26:18 -0700135 if (ec)
136 {
137 return; // threshold not supported
138 }
139
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800140 if (!labelMatch.empty())
141 {
142 auto labelFind = result.find("Label");
143 if (labelFind == result.end())
144 {
145 std::cerr << "No label in threshold configuration\n";
146 return;
147 }
148 std::string label =
149 std::visit(VariantToStringVisitor(), labelFind->second);
150 if (label != labelMatch)
151 {
152 return;
153 }
154 }
155
James Feist6714a252018-09-10 15:26:18 -0700156 auto directionFind = result.find("Direction");
157 auto severityFind = result.find("Severity");
158 auto valueFind = result.find("Value");
159 if (valueFind == result.end() || severityFind == result.end() ||
160 directionFind == result.end())
161 {
162 std::cerr << "Malformed threshold in configuration\n";
163 return;
164 }
James Feist3eb82622019-02-08 13:10:22 -0800165 unsigned int level = std::visit(VariantToUnsignedIntVisitor(),
166 severityFind->second);
James Feist6714a252018-09-10 15:26:18 -0700167
James Feist3eb82622019-02-08 13:10:22 -0800168 std::string dir =
169 std::visit(VariantToStringVisitor(), directionFind->second);
James Feist6714a252018-09-10 15:26:18 -0700170 if ((toBusValue(threshold.level) != level) ||
171 (toBusValue(threshold.direction) != dir))
172 {
173 return; // not the droid we're looking for
174 }
175
James Feist3eb82622019-02-08 13:10:22 -0800176 std::variant<double> value(threshold.value);
James Feist6714a252018-09-10 15:26:18 -0700177 conn->async_method_call(
James Feistd8705872019-02-08 13:26:09 -0800178 [](const boost::system::error_code& ec) {
James Feist6714a252018-09-10 15:26:18 -0700179 if (ec)
180 {
181 std::cerr << "Error setting threshold " << ec
182 << "\n";
183 }
184 },
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700185 entityManagerName, path, "org.freedesktop.DBus.Properties",
186 "Set", thresholdInterface, "Value", value);
James Feist6714a252018-09-10 15:26:18 -0700187 },
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700188 entityManagerName, path, "org.freedesktop.DBus.Properties",
James Feist6714a252018-09-10 15:26:18 -0700189 "GetAll", thresholdInterface);
190 }
191}
192
Jae Hyun Yoo95b8a2d2019-02-25 20:15:09 -0800193void updateThresholds(Sensor* sensor)
194{
195 if (sensor->thresholds.empty())
196 {
197 return;
198 }
199
200 for (const auto& threshold : sensor->thresholds)
201 {
202 std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
203 std::string property;
204 if (threshold.level == thresholds::Level::CRITICAL)
205 {
206 interface = sensor->thresholdInterfaceCritical;
207 if (threshold.direction == thresholds::Direction::HIGH)
208 {
209 property = "CriticalHigh";
210 }
211 else
212 {
213 property = "CriticalLow";
214 }
215 }
216 else if (threshold.level == thresholds::Level::WARNING)
217 {
218 interface = sensor->thresholdInterfaceWarning;
219 if (threshold.direction == thresholds::Direction::HIGH)
220 {
221 property = "WarningHigh";
222 }
223 else
224 {
225 property = "WarningLow";
226 }
227 }
228 else
229 {
230 continue;
231 }
232 if (!interface)
233 {
234 continue;
235 }
236 interface->set_property(property, threshold.value);
237 }
238}
239
Josh Lehan883fb3a2020-02-27 14:41:39 -0800240// Debugging counters
241static int cHiTrue = 0;
242static int cHiFalse = 0;
243static int cHiMidstate = 0;
244static int cLoTrue = 0;
245static int cLoFalse = 0;
246static int cLoMidstate = 0;
247static int cDebugThrottle = 0;
Zhikui Rend3da1282020-09-11 17:02:01 -0700248static constexpr int assertLogCount = 10;
Josh Lehan883fb3a2020-02-27 14:41:39 -0800249
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700250struct ChangeParam
James Feist251c7822018-09-12 12:54:15 -0700251{
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700252 ChangeParam(Threshold whichThreshold, bool status, double value) :
253 threshold(whichThreshold), asserted(status), assertValue(value)
254 {}
255
256 Threshold threshold;
257 bool asserted;
258 double assertValue;
259};
260
261static std::vector<ChangeParam> checkThresholds(Sensor* sensor, double value)
262{
263 std::vector<ChangeParam> thresholdChanges;
James Feist251c7822018-09-12 12:54:15 -0700264 if (sensor->thresholds.empty())
265 {
James Feist46342ec2019-03-06 14:03:41 -0800266 return thresholdChanges;
James Feist251c7822018-09-12 12:54:15 -0700267 }
James Feist46342ec2019-03-06 14:03:41 -0800268
James Feistd8705872019-02-08 13:26:09 -0800269 for (auto& threshold : sensor->thresholds)
James Feist251c7822018-09-12 12:54:15 -0700270 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800271 // Use "Schmitt trigger" logic to avoid threshold trigger spam,
272 // if value is noisy while hovering very close to a threshold.
273 // When a threshold is crossed, indicate true immediately,
274 // but require more distance to be crossed the other direction,
275 // before resetting the indicator back to false.
James Feist46342ec2019-03-06 14:03:41 -0800276 if (threshold.direction == thresholds::Direction::HIGH)
James Feistdc6c55f2018-10-31 12:53:20 -0700277 {
James Feist551087a2019-12-09 11:17:12 -0800278 if (value >= threshold.value)
James Feist251c7822018-09-12 12:54:15 -0700279 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700280 thresholdChanges.emplace_back(threshold, true, value);
Zhikui Rend3da1282020-09-11 17:02:01 -0700281 if (++cHiTrue < assertLogCount)
282 {
283 std::cerr << "Sensor " << sensor->name << " high threshold "
284 << threshold.value << " assert: value " << value
285 << " raw data " << sensor->rawValue << "\n";
286 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800287 }
288 else if (value < (threshold.value - sensor->hysteresisTrigger))
289 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700290 thresholdChanges.emplace_back(threshold, false, value);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800291 ++cHiFalse;
James Feist251c7822018-09-12 12:54:15 -0700292 }
Patrick Venture66235d42019-10-11 08:31:27 -0700293 else
James Feist251c7822018-09-12 12:54:15 -0700294 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800295 ++cHiMidstate;
James Feist251c7822018-09-12 12:54:15 -0700296 }
297 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800298 else if (threshold.direction == thresholds::Direction::LOW)
James Feist251c7822018-09-12 12:54:15 -0700299 {
James Feist551087a2019-12-09 11:17:12 -0800300 if (value <= threshold.value)
James Feist251c7822018-09-12 12:54:15 -0700301 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700302 thresholdChanges.emplace_back(threshold, true, value);
Zhikui Rend3da1282020-09-11 17:02:01 -0700303 if (++cLoTrue < assertLogCount)
304 {
305 std::cerr << "Sensor " << sensor->name << " low threshold "
306 << threshold.value << " assert: value "
307 << sensor->value << " raw data "
308 << sensor->rawValue << "\n";
309 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800310 }
311 else if (value > (threshold.value + sensor->hysteresisTrigger))
312 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700313 thresholdChanges.emplace_back(threshold, false, value);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800314 ++cLoFalse;
James Feist251c7822018-09-12 12:54:15 -0700315 }
Patrick Venture66235d42019-10-11 08:31:27 -0700316 else
James Feist251c7822018-09-12 12:54:15 -0700317 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800318 ++cLoMidstate;
James Feist251c7822018-09-12 12:54:15 -0700319 }
320 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800321 else
322 {
323 std::cerr << "Error determining threshold direction\n";
324 }
James Feist251c7822018-09-12 12:54:15 -0700325 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800326
Ed Tanous8a57ec02020-10-09 12:46:52 -0700327 if constexpr (debug)
Josh Lehan883fb3a2020-02-27 14:41:39 -0800328 {
329 // Throttle debug output, so that it does not continuously spam
330 ++cDebugThrottle;
331 if (cDebugThrottle >= 1000)
332 {
333 cDebugThrottle = 0;
334 std::cerr << "checkThresholds: High T=" << cHiTrue
335 << " F=" << cHiFalse << " M=" << cHiMidstate
336 << ", Low T=" << cLoTrue << " F=" << cLoFalse
337 << " M=" << cLoMidstate << "\n";
338 }
339 }
340
James Feist46342ec2019-03-06 14:03:41 -0800341 return thresholdChanges;
342}
343
344bool checkThresholds(Sensor* sensor)
345{
James Feist7b18b1e2019-05-14 13:42:09 -0700346 bool status = true;
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700347 std::vector<ChangeParam> changes = checkThresholds(sensor, sensor->value);
348 for (const auto& change : changes)
James Feist46342ec2019-03-06 14:03:41 -0800349 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700350 assertThresholds(sensor, change.assertValue, change.threshold.level,
351 change.threshold.direction, change.asserted);
352 if (change.threshold.level == thresholds::Level::CRITICAL &&
353 change.asserted)
James Feist46342ec2019-03-06 14:03:41 -0800354 {
James Feist7b18b1e2019-05-14 13:42:09 -0700355 status = false;
James Feist46342ec2019-03-06 14:03:41 -0800356 }
357 }
358
James Feistdc6c55f2018-10-31 12:53:20 -0700359 return status;
James Feist251c7822018-09-12 12:54:15 -0700360}
361
James Feist46342ec2019-03-06 14:03:41 -0800362void checkThresholdsPowerDelay(Sensor* sensor, ThresholdTimer& thresholdTimer)
363{
364
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700365 std::vector<ChangeParam> changes = checkThresholds(sensor, sensor->value);
366 for (const auto& change : changes)
James Feist46342ec2019-03-06 14:03:41 -0800367 {
Zhikui Renbf7cbc82020-07-02 08:44:00 -0700368 // When CPU is powered off, some volatges are expected to
369 // go below low thresholds. Filter these events with thresholdTimer.
370 // 1. always delay the assertion of low events to see if they are
371 // caused by power off event.
372 // 2. conditional delay the de-assertion of low events if there is
373 // an existing timer for assertion.
374 // 3. no delays for de-assert of low events if there is an existing
375 // de-assert for low event. This means 2nd de-assert would happen
376 // first and when timer expires for the previous one, no additional
377 // signal will be logged.
378 // 4. no delays for all high events.
379 if (change.threshold.direction == thresholds::Direction::LOW)
James Feist46342ec2019-03-06 14:03:41 -0800380 {
Zhikui Renbf7cbc82020-07-02 08:44:00 -0700381 if (change.asserted || thresholdTimer.hasActiveTimer(
382 change.threshold, !change.asserted))
383 {
384 thresholdTimer.startTimer(change.threshold, change.asserted,
385 change.assertValue);
386 continue;
387 }
James Feist46342ec2019-03-06 14:03:41 -0800388 }
Zhikui Renbf7cbc82020-07-02 08:44:00 -0700389 assertThresholds(sensor, change.assertValue, change.threshold.level,
390 change.threshold.direction, change.asserted);
James Feist46342ec2019-03-06 14:03:41 -0800391 }
392}
393
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700394void assertThresholds(Sensor* sensor, double assertValue,
395 thresholds::Level level, thresholds::Direction direction,
396 bool assert)
James Feist251c7822018-09-12 12:54:15 -0700397{
398 std::string property;
399 std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
400 if (level == thresholds::Level::WARNING &&
401 direction == thresholds::Direction::HIGH)
402 {
403 property = "WarningAlarmHigh";
404 interface = sensor->thresholdInterfaceWarning;
405 }
406 else if (level == thresholds::Level::WARNING &&
407 direction == thresholds::Direction::LOW)
408 {
409 property = "WarningAlarmLow";
410 interface = sensor->thresholdInterfaceWarning;
411 }
412 else if (level == thresholds::Level::CRITICAL &&
413 direction == thresholds::Direction::HIGH)
414 {
415 property = "CriticalAlarmHigh";
416 interface = sensor->thresholdInterfaceCritical;
417 }
418 else if (level == thresholds::Level::CRITICAL &&
419 direction == thresholds::Direction::LOW)
420 {
421 property = "CriticalAlarmLow";
422 interface = sensor->thresholdInterfaceCritical;
423 }
424 else
425 {
426 std::cerr << "Unknown threshold, level " << level << "direction "
427 << direction << "\n";
428 return;
429 }
430 if (!interface)
431 {
432 std::cout << "trying to set uninitialized interface\n";
433 return;
434 }
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700435
436 if (interface->set_property<bool, true>(property, assert))
437 {
438 try
439 {
440 // msg.get_path() is interface->get_object_path()
441 sdbusplus::message::message msg =
442 interface->new_signal("ThresholdAsserted");
443
444 msg.append(sensor->name, interface->get_interface_name(), property,
445 assert, assertValue);
446 msg.signal_send();
447 }
448 catch (const sdbusplus::exception::exception& e)
449 {
450 std::cerr
451 << "Failed to send thresholdAsserted signal with assertValue\n";
452 }
453 }
James Feist251c7822018-09-12 12:54:15 -0700454}
455
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700456bool parseThresholdsFromAttr(
James Feistd8705872019-02-08 13:26:09 -0800457 std::vector<thresholds::Threshold>& thresholdVector,
Vijay Khemka86dea2b2019-06-06 11:14:37 -0700458 const std::string& inputPath, const double& scaleFactor,
459 const double& offset)
James Feist6714a252018-09-10 15:26:18 -0700460{
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +0200461 const boost::container::flat_map<
462 std::string, std::vector<std::tuple<const char*, thresholds::Level,
463 thresholds::Direction, double>>>
464 map = {
465 {"average",
466 {
467 std::make_tuple("average_min", Level::WARNING, Direction::LOW,
468 0.0),
469 std::make_tuple("average_max", Level::WARNING, Direction::HIGH,
470 0.0),
471 }},
472 {"input",
473 {
474 std::make_tuple("min", Level::WARNING, Direction::LOW, 0.0),
475 std::make_tuple("max", Level::WARNING, Direction::HIGH, 0.0),
476 std::make_tuple("lcrit", Level::CRITICAL, Direction::LOW, 0.0),
477 std::make_tuple("crit", Level::CRITICAL, Direction::HIGH,
478 offset),
479 }},
480 };
481
482 if (auto fileParts = splitFileName(inputPath))
James Feist6714a252018-09-10 15:26:18 -0700483 {
Zbigniew Kurzynskidbfd4662020-09-28 18:06:00 +0200484 auto& [type, nr, item] = *fileParts;
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +0200485 if (map.count(item) != 0)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700486 {
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +0200487 for (const auto& t : map.at(item))
488 {
Zbigniew Kurzynskidbfd4662020-09-28 18:06:00 +0200489 auto& [suffix, level, direction, offset] = t;
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +0200490 auto attrPath =
491 boost::replace_all_copy(inputPath, item, suffix);
492 if (auto val = readFile(attrPath, scaleFactor))
493 {
494 *val += offset;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700495 if (debug)
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +0200496 {
497 std::cout << "Threshold: " << attrPath << ": " << *val
498 << "\n";
499 }
500 thresholdVector.emplace_back(level, direction, *val);
501 }
502 }
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700503 }
James Feist6714a252018-09-10 15:26:18 -0700504 }
James Feist6714a252018-09-10 15:26:18 -0700505 return true;
506}
507
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700508bool hasCriticalInterface(
James Feistd8705872019-02-08 13:26:09 -0800509 const std::vector<thresholds::Threshold>& thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700510{
James Feistd8705872019-02-08 13:26:09 -0800511 for (auto& threshold : thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700512 {
513 if (threshold.level == Level::CRITICAL)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700514 {
James Feist6714a252018-09-10 15:26:18 -0700515 return true;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700516 }
James Feist6714a252018-09-10 15:26:18 -0700517 }
518 return false;
519}
520
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700521bool hasWarningInterface(
James Feistd8705872019-02-08 13:26:09 -0800522 const std::vector<thresholds::Threshold>& thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700523{
James Feistd8705872019-02-08 13:26:09 -0800524 for (auto& threshold : thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700525 {
526 if (threshold.level == Level::WARNING)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700527 {
James Feist6714a252018-09-10 15:26:18 -0700528 return true;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700529 }
James Feist6714a252018-09-10 15:26:18 -0700530 }
531 return false;
532}
533} // namespace thresholds