blob: b37698e7804a1e2ca8ed530c23044240b5a8c714 [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 {
63 error("Invalid Memory metric {TYPE}", "TYPE",
64 std::to_underlying(subType));
65 return "";
66 }
67 }
68}
69
70void HealthMetric::initProperties()
71{
72 switch (config.subType)
73 {
74 case SubType::cpuTotal:
75 case SubType::cpuKernel:
76 case SubType::cpuUser:
77 {
78 ValueIntf::unit(ValueIntf::Unit::Percent, true);
79 ValueIntf::minValue(0.0, true);
80 ValueIntf::maxValue(100.0, true);
81 break;
82 }
83 case SubType::memoryAvailable:
84 case SubType::memoryBufferedAndCached:
85 case SubType::memoryFree:
86 case SubType::memoryShared:
87 case SubType::memoryTotal:
88 case SubType::storageReadWrite:
89 default:
90 {
91 ValueIntf::unit(ValueIntf::Unit::Bytes, true);
92 ValueIntf::minValue(0.0, true);
93 }
94 }
Jagpal Singh Gillc5b18bc2024-02-09 15:58:12 -080095 ValueIntf::value(std::numeric_limits<double>::quiet_NaN(), true);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -080096
97 using bound_map_t = std::map<ThresholdIntf::Bound, double>;
98 std::map<ThresholdIntf::Type, bound_map_t> thresholds;
99 for (const auto& [key, value] : config.thresholds)
100 {
101 auto type = std::get<ThresholdIntf::Type>(key);
102 auto bound = std::get<ThresholdIntf::Bound>(key);
103 auto threshold = thresholds.find(type);
104 if (threshold == thresholds.end())
105 {
106 bound_map_t bounds;
107 bounds.emplace(bound, value.value);
108 thresholds.emplace(type, bounds);
109 }
110 else
111 {
112 threshold->second.emplace(bound, value.value);
113 }
114 }
Jagpal Singh Gillc5b18bc2024-02-09 15:58:12 -0800115 ThresholdIntf::value(thresholds, true);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800116}
117
118void HealthMetric::checkThreshold(ThresholdIntf::Type type,
119 ThresholdIntf::Bound bound, double value)
120{
121 auto threshold = std::make_tuple(type, bound);
122 auto thresholds = ThresholdIntf::value();
123
124 if (thresholds.contains(type) && thresholds[type].contains(bound))
125 {
126 auto thresholdValue = thresholds[type][bound];
127 auto assertions = ThresholdIntf::asserted();
128 if (value > thresholdValue)
129 {
130 if (!assertions.contains(threshold))
131 {
132 assertions.insert(threshold);
133 ThresholdIntf::asserted(assertions);
134 ThresholdIntf::assertionChanged(type, bound, true, value);
135 auto tConfig = config.thresholds.at(threshold);
136 if (tConfig.log)
137 {
138 error(
139 "ASSERT: Health Metric {METRIC} crossed {TYPE} upper threshold",
140 "METRIC", config.name, "TYPE",
141 sdbusplus::message::convert_to_string(type));
142 startUnit(bus, tConfig.target);
143 }
144 }
145 return;
146 }
147 else if (assertions.contains(threshold))
148 {
149 assertions.erase(threshold);
150 ThresholdIntf::asserted(assertions);
151 ThresholdIntf::assertionChanged(type, bound, false, value);
152 if (config.thresholds.find(threshold)->second.log)
153 {
154 info(
155 "DEASSERT: Health Metric {METRIC} is below {TYPE} upper threshold",
156 "METRIC", config.name, "TYPE",
157 sdbusplus::message::convert_to_string(type));
158 }
159 }
160 }
161}
162
163void HealthMetric::checkThresholds(double value)
164{
165 if (!ThresholdIntf::value().empty())
166 {
167 for (auto type :
168 {ThresholdIntf::Type::HardShutdown,
169 ThresholdIntf::Type::SoftShutdown,
170 ThresholdIntf::Type::PerformanceLoss,
171 ThresholdIntf::Type::Critical, ThresholdIntf::Type::Warning})
172 {
173 checkThreshold(type, ThresholdIntf::Bound::Upper, value);
174 }
175 }
176}
177
178void HealthMetric::update(MValue value)
179{
180 // Maintain window size for metric
181 if (history.size() >= config.windowSize)
182 {
183 history.pop_front();
184 }
185 history.push_back(value.user);
186
187 if (history.size() < config.windowSize)
188 {
189 // Wait for the metric to have enough samples to calculate average
190 info("Not enough samples to calculate average");
191 return;
192 }
193
194 double average = (std::accumulate(history.begin(), history.end(), 0.0)) /
195 history.size();
196 ValueIntf::value(average);
197 checkThresholds(value.monitor);
198}
199
200void HealthMetric::create(const paths_t& bmcPaths)
201{
202 info("Create Health Metric: {METRIC}", "METRIC", config.name);
203 initProperties();
204
205 std::vector<association_t> associations;
206 static constexpr auto forwardAssociation = "measuring";
207 static constexpr auto reverseAssociation = "measured_by";
208 for (const auto& bmcPath : bmcPaths)
209 {
210 /*
211 * This metric is "measuring" the health for the BMC at bmcPath
212 * The BMC at bmcPath is "measured_by" this metric.
213 */
214 associations.push_back(
215 {forwardAssociation, reverseAssociation, bmcPath});
216 }
217 AssociationIntf::associations(associations);
218}
219
220} // namespace phosphor::health::metric