blob: 8ee427bcbb76ad12e362adf510d0838928c5fc00 [file] [log] [blame]
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -08001#include "health_metric.hpp"
2
3#include <phosphor-logging/lg2.hpp>
4
5#include <numeric>
6#include <unordered_map>
7
8PHOSPHOR_LOG2_USING;
9
10namespace phosphor::health::metric
11{
12
13using association_t = std::tuple<std::string, std::string, std::string>;
14
15auto HealthMetric::getPath(SubType subType) -> std::string
16{
17 std::string path;
18 switch (subType)
19 {
20 case SubType::cpuTotal:
21 {
22 return std::string(BmcPath) + "/" + PathIntf::total_cpu;
23 }
24 case SubType::cpuKernel:
25 {
26 return std::string(BmcPath) + "/" + PathIntf::kernel_cpu;
27 }
28 case SubType::cpuUser:
29 {
30 return std::string(BmcPath) + "/" + PathIntf::user_cpu;
31 }
32 case SubType::memoryAvailable:
33 {
34 return std::string(BmcPath) + "/" + PathIntf::available_memory;
35 }
36 case SubType::memoryBufferedAndCached:
37 {
38 return std::string(BmcPath) + "/" +
39 PathIntf::buffered_and_cached_memory;
40 }
41 case SubType::memoryFree:
42 {
43 return std::string(BmcPath) + "/" + PathIntf::free_memory;
44 }
45 case SubType::memoryShared:
46 {
47 return std::string(BmcPath) + "/" + PathIntf::shared_memory;
48 }
49 case SubType::memoryTotal:
50 {
51 return std::string(BmcPath) + "/" + PathIntf::total_memory;
52 }
53 case SubType::storageReadWrite:
54 {
55 return std::string(BmcPath) + "/" + PathIntf::read_write_storage;
56 }
Jagpal Singh Gilldfe839f2024-02-16 09:54:02 -080057 case SubType::storageTmp:
58 {
59 return std::string(BmcPath) + "/" + PathIntf::tmp_storage;
60 }
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -080061 default:
62 {
Patrick Williams67b8ebe2024-02-23 20:40:52 -060063 error("Invalid Memory metric {TYPE}", "TYPE", subType);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -080064 return "";
65 }
66 }
67}
68
69void HealthMetric::initProperties()
70{
71 switch (config.subType)
72 {
73 case SubType::cpuTotal:
74 case SubType::cpuKernel:
75 case SubType::cpuUser:
76 {
77 ValueIntf::unit(ValueIntf::Unit::Percent, true);
78 ValueIntf::minValue(0.0, true);
79 ValueIntf::maxValue(100.0, true);
80 break;
81 }
82 case SubType::memoryAvailable:
83 case SubType::memoryBufferedAndCached:
84 case SubType::memoryFree:
85 case SubType::memoryShared:
86 case SubType::memoryTotal:
87 case SubType::storageReadWrite:
88 default:
89 {
90 ValueIntf::unit(ValueIntf::Unit::Bytes, true);
91 ValueIntf::minValue(0.0, true);
92 }
93 }
Jagpal Singh Gillc5b18bc2024-02-09 15:58:12 -080094 ValueIntf::value(std::numeric_limits<double>::quiet_NaN(), true);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -080095
96 using bound_map_t = std::map<ThresholdIntf::Bound, double>;
97 std::map<ThresholdIntf::Type, bound_map_t> thresholds;
98 for (const auto& [key, value] : config.thresholds)
99 {
100 auto type = std::get<ThresholdIntf::Type>(key);
101 auto bound = std::get<ThresholdIntf::Bound>(key);
102 auto threshold = thresholds.find(type);
103 if (threshold == thresholds.end())
104 {
105 bound_map_t bounds;
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800106 bounds.emplace(bound, std::numeric_limits<double>::quiet_NaN());
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800107 thresholds.emplace(type, bounds);
108 }
109 else
110 {
111 threshold->second.emplace(bound, value.value);
112 }
113 }
Jagpal Singh Gillc5b18bc2024-02-09 15:58:12 -0800114 ThresholdIntf::value(thresholds, true);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800115}
116
Jagpal Singh Gill55fb0c92024-02-22 18:07:13 -0800117bool didThresholdViolate(ThresholdIntf::Bound bound, double thresholdValue,
118 double value)
119{
120 switch (bound)
121 {
122 case ThresholdIntf::Bound::Lower:
123 {
124 return (value < thresholdValue);
125 }
126 case ThresholdIntf::Bound::Upper:
127 {
128 return (value > thresholdValue);
129 }
130 default:
131 {
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600132 error("Invalid threshold bound {BOUND}", "BOUND", bound);
Jagpal Singh Gill55fb0c92024-02-22 18:07:13 -0800133 return false;
134 }
135 }
136}
137
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800138void HealthMetric::checkThreshold(ThresholdIntf::Type type,
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800139 ThresholdIntf::Bound bound, MValue value)
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800140{
141 auto threshold = std::make_tuple(type, bound);
142 auto thresholds = ThresholdIntf::value();
143
144 if (thresholds.contains(type) && thresholds[type].contains(bound))
145 {
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800146 auto tConfig = config.thresholds.at(threshold);
147 auto thresholdValue = tConfig.value / 100 * value.total;
148 thresholds[type][bound] = thresholdValue;
149 ThresholdIntf::value(thresholds);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800150 auto assertions = ThresholdIntf::asserted();
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800151 if (didThresholdViolate(bound, thresholdValue, value.current))
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800152 {
153 if (!assertions.contains(threshold))
154 {
155 assertions.insert(threshold);
156 ThresholdIntf::asserted(assertions);
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800157 ThresholdIntf::assertionChanged(type, bound, true,
158 value.current);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800159 if (tConfig.log)
160 {
161 error(
162 "ASSERT: Health Metric {METRIC} crossed {TYPE} upper threshold",
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600163 "METRIC", config.name, "TYPE", type);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800164 startUnit(bus, tConfig.target);
165 }
166 }
167 return;
168 }
169 else if (assertions.contains(threshold))
170 {
171 assertions.erase(threshold);
172 ThresholdIntf::asserted(assertions);
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800173 ThresholdIntf::assertionChanged(type, bound, false, value.current);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800174 if (config.thresholds.find(threshold)->second.log)
175 {
176 info(
177 "DEASSERT: Health Metric {METRIC} is below {TYPE} upper threshold",
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600178 "METRIC", config.name, "TYPE", type);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800179 }
180 }
181 }
182}
183
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800184void HealthMetric::checkThresholds(MValue value)
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800185{
186 if (!ThresholdIntf::value().empty())
187 {
188 for (auto type :
189 {ThresholdIntf::Type::HardShutdown,
190 ThresholdIntf::Type::SoftShutdown,
191 ThresholdIntf::Type::PerformanceLoss,
192 ThresholdIntf::Type::Critical, ThresholdIntf::Type::Warning})
193 {
Jagpal Singh Gill55fb0c92024-02-22 18:07:13 -0800194 checkThreshold(type, ThresholdIntf::Bound::Lower, value);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800195 checkThreshold(type, ThresholdIntf::Bound::Upper, value);
196 }
197 }
198}
199
200void HealthMetric::update(MValue value)
201{
202 // Maintain window size for metric
203 if (history.size() >= config.windowSize)
204 {
205 history.pop_front();
206 }
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800207 history.push_back(value.current);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800208
209 if (history.size() < config.windowSize)
210 {
211 // Wait for the metric to have enough samples to calculate average
Patrick Williams0f54d7a2024-02-22 12:39:46 -0600212 debug("Not enough samples to calculate average");
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800213 return;
214 }
215
216 double average = (std::accumulate(history.begin(), history.end(), 0.0)) /
217 history.size();
218 ValueIntf::value(average);
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800219 checkThresholds(value);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800220}
221
222void HealthMetric::create(const paths_t& bmcPaths)
223{
224 info("Create Health Metric: {METRIC}", "METRIC", config.name);
225 initProperties();
226
227 std::vector<association_t> associations;
228 static constexpr auto forwardAssociation = "measuring";
229 static constexpr auto reverseAssociation = "measured_by";
230 for (const auto& bmcPath : bmcPaths)
231 {
232 /*
233 * This metric is "measuring" the health for the BMC at bmcPath
234 * The BMC at bmcPath is "measured_by" this metric.
235 */
236 associations.push_back(
237 {forwardAssociation, reverseAssociation, bmcPath});
238 }
239 AssociationIntf::associations(associations);
240}
241
242} // namespace phosphor::health::metric