blob: e29954a3a73c8a3536eb565f7a6d80de0a2735c3 [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
Jagpal Singh Gill97582802024-02-27 13:59:11 -080015auto HealthMetric::getPath(phosphor::health::metric::Type type,
16 std::string name, SubType subType) -> std::string
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -080017{
18 std::string path;
19 switch (subType)
20 {
21 case SubType::cpuTotal:
22 {
23 return std::string(BmcPath) + "/" + PathIntf::total_cpu;
24 }
25 case SubType::cpuKernel:
26 {
27 return std::string(BmcPath) + "/" + PathIntf::kernel_cpu;
28 }
29 case SubType::cpuUser:
30 {
31 return std::string(BmcPath) + "/" + PathIntf::user_cpu;
32 }
33 case SubType::memoryAvailable:
34 {
35 return std::string(BmcPath) + "/" + PathIntf::available_memory;
36 }
37 case SubType::memoryBufferedAndCached:
38 {
39 return std::string(BmcPath) + "/" +
40 PathIntf::buffered_and_cached_memory;
41 }
42 case SubType::memoryFree:
43 {
44 return std::string(BmcPath) + "/" + PathIntf::free_memory;
45 }
46 case SubType::memoryShared:
47 {
48 return std::string(BmcPath) + "/" + PathIntf::shared_memory;
49 }
50 case SubType::memoryTotal:
51 {
52 return std::string(BmcPath) + "/" + PathIntf::total_memory;
53 }
Jagpal Singh Gill97582802024-02-27 13:59:11 -080054 case SubType::NA:
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -080055 {
Jagpal Singh Gill97582802024-02-27 13:59:11 -080056 if (type == phosphor::health::metric::Type::storage)
57 {
58 static constexpr auto nameDelimiter = "_";
59 auto storageType = name.substr(
60 name.find_last_of(nameDelimiter) + 1, name.length());
61 std::ranges::for_each(storageType,
62 [](auto& c) { c = std::tolower(c); });
63 return std::string(BmcPath) + "/" + PathIntf::storage + "/" +
64 storageType;
65 }
66 else
67 {
68 error("Invalid metric {SUBTYPE} for metric {TYPE}", "SUBTYPE",
69 subType, "TYPE", type);
70 return "";
71 }
Jagpal Singh Gilldfe839f2024-02-16 09:54:02 -080072 }
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -080073 default:
74 {
Jagpal Singh Gill97582802024-02-27 13:59:11 -080075 error("Invalid metric {SUBTYPE}", "SUBTYPE", subType);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -080076 return "";
77 }
78 }
79}
80
81void HealthMetric::initProperties()
82{
Jagpal Singh Gill97582802024-02-27 13:59:11 -080083 switch (type)
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -080084 {
Jagpal Singh Gill97582802024-02-27 13:59:11 -080085 case MType::cpu:
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -080086 {
87 ValueIntf::unit(ValueIntf::Unit::Percent, true);
88 ValueIntf::minValue(0.0, true);
89 ValueIntf::maxValue(100.0, true);
90 break;
91 }
Jagpal Singh Gill97582802024-02-27 13:59:11 -080092 case MType::memory:
93 case MType::storage:
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -080094 {
95 ValueIntf::unit(ValueIntf::Unit::Bytes, true);
96 ValueIntf::minValue(0.0, true);
Jagpal Singh Gill97582802024-02-27 13:59:11 -080097 break;
98 }
99 case MType::inode:
100 case MType::unknown:
101 default:
102 {
103 throw std::invalid_argument("Invalid metric type");
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800104 }
105 }
Jagpal Singh Gillc5b18bc2024-02-09 15:58:12 -0800106 ValueIntf::value(std::numeric_limits<double>::quiet_NaN(), true);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800107
108 using bound_map_t = std::map<ThresholdIntf::Bound, double>;
109 std::map<ThresholdIntf::Type, bound_map_t> thresholds;
110 for (const auto& [key, value] : config.thresholds)
111 {
112 auto type = std::get<ThresholdIntf::Type>(key);
113 auto bound = std::get<ThresholdIntf::Bound>(key);
114 auto threshold = thresholds.find(type);
115 if (threshold == thresholds.end())
116 {
117 bound_map_t bounds;
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800118 bounds.emplace(bound, std::numeric_limits<double>::quiet_NaN());
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800119 thresholds.emplace(type, bounds);
120 }
121 else
122 {
123 threshold->second.emplace(bound, value.value);
124 }
125 }
Jagpal Singh Gillc5b18bc2024-02-09 15:58:12 -0800126 ThresholdIntf::value(thresholds, true);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800127}
128
Jagpal Singh Gill55fb0c92024-02-22 18:07:13 -0800129bool didThresholdViolate(ThresholdIntf::Bound bound, double thresholdValue,
130 double value)
131{
132 switch (bound)
133 {
134 case ThresholdIntf::Bound::Lower:
135 {
136 return (value < thresholdValue);
137 }
138 case ThresholdIntf::Bound::Upper:
139 {
140 return (value > thresholdValue);
141 }
142 default:
143 {
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600144 error("Invalid threshold bound {BOUND}", "BOUND", bound);
Jagpal Singh Gill55fb0c92024-02-22 18:07:13 -0800145 return false;
146 }
147 }
148}
149
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800150void HealthMetric::checkThreshold(ThresholdIntf::Type type,
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800151 ThresholdIntf::Bound bound, MValue value)
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800152{
153 auto threshold = std::make_tuple(type, bound);
154 auto thresholds = ThresholdIntf::value();
155
156 if (thresholds.contains(type) && thresholds[type].contains(bound))
157 {
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800158 auto tConfig = config.thresholds.at(threshold);
159 auto thresholdValue = tConfig.value / 100 * value.total;
160 thresholds[type][bound] = thresholdValue;
161 ThresholdIntf::value(thresholds);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800162 auto assertions = ThresholdIntf::asserted();
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800163 if (didThresholdViolate(bound, thresholdValue, value.current))
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800164 {
165 if (!assertions.contains(threshold))
166 {
167 assertions.insert(threshold);
168 ThresholdIntf::asserted(assertions);
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800169 ThresholdIntf::assertionChanged(type, bound, true,
170 value.current);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800171 if (tConfig.log)
172 {
173 error(
174 "ASSERT: Health Metric {METRIC} crossed {TYPE} upper threshold",
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600175 "METRIC", config.name, "TYPE", type);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800176 startUnit(bus, tConfig.target);
177 }
178 }
179 return;
180 }
181 else if (assertions.contains(threshold))
182 {
183 assertions.erase(threshold);
184 ThresholdIntf::asserted(assertions);
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800185 ThresholdIntf::assertionChanged(type, bound, false, value.current);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800186 if (config.thresholds.find(threshold)->second.log)
187 {
188 info(
189 "DEASSERT: Health Metric {METRIC} is below {TYPE} upper threshold",
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600190 "METRIC", config.name, "TYPE", type);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800191 }
192 }
193 }
194}
195
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800196void HealthMetric::checkThresholds(MValue value)
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800197{
198 if (!ThresholdIntf::value().empty())
199 {
200 for (auto type :
201 {ThresholdIntf::Type::HardShutdown,
202 ThresholdIntf::Type::SoftShutdown,
203 ThresholdIntf::Type::PerformanceLoss,
204 ThresholdIntf::Type::Critical, ThresholdIntf::Type::Warning})
205 {
Jagpal Singh Gill55fb0c92024-02-22 18:07:13 -0800206 checkThreshold(type, ThresholdIntf::Bound::Lower, value);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800207 checkThreshold(type, ThresholdIntf::Bound::Upper, value);
208 }
209 }
210}
211
212void HealthMetric::update(MValue value)
213{
214 // Maintain window size for metric
215 if (history.size() >= config.windowSize)
216 {
217 history.pop_front();
218 }
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800219 history.push_back(value.current);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800220
221 if (history.size() < config.windowSize)
222 {
223 // Wait for the metric to have enough samples to calculate average
Patrick Williams0f54d7a2024-02-22 12:39:46 -0600224 debug("Not enough samples to calculate average");
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800225 return;
226 }
227
228 double average = (std::accumulate(history.begin(), history.end(), 0.0)) /
229 history.size();
230 ValueIntf::value(average);
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800231 checkThresholds(value);
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -0800232}
233
234void HealthMetric::create(const paths_t& bmcPaths)
235{
236 info("Create Health Metric: {METRIC}", "METRIC", config.name);
237 initProperties();
238
239 std::vector<association_t> associations;
240 static constexpr auto forwardAssociation = "measuring";
241 static constexpr auto reverseAssociation = "measured_by";
242 for (const auto& bmcPath : bmcPaths)
243 {
244 /*
245 * This metric is "measuring" the health for the BMC at bmcPath
246 * The BMC at bmcPath is "measured_by" this metric.
247 */
248 associations.push_back(
249 {forwardAssociation, reverseAssociation, bmcPath});
250 }
251 AssociationIntf::associations(associations);
252}
253
254} // namespace phosphor::health::metric