1 #include "health_metric.hpp" 2 3 #include <phosphor-logging/lg2.hpp> 4 5 #include <numeric> 6 #include <unordered_map> 7 8 PHOSPHOR_LOG2_USING; 9 10 namespace phosphor::health::metric 11 { 12 13 using association_t = std::tuple<std::string, std::string, std::string>; 14 15 auto 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 } 57 case SubType::storageTmp: 58 { 59 return std::string(BmcPath) + "/" + PathIntf::tmp_storage; 60 } 61 default: 62 { 63 error("Invalid Memory metric {TYPE}", "TYPE", 64 std::to_underlying(subType)); 65 return ""; 66 } 67 } 68 } 69 70 void 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 } 95 ValueIntf::value(std::numeric_limits<double>::quiet_NaN(), true); 96 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 } 115 ThresholdIntf::value(thresholds, true); 116 } 117 118 bool didThresholdViolate(ThresholdIntf::Bound bound, double thresholdValue, 119 double value) 120 { 121 switch (bound) 122 { 123 case ThresholdIntf::Bound::Lower: 124 { 125 return (value < thresholdValue); 126 } 127 case ThresholdIntf::Bound::Upper: 128 { 129 return (value > thresholdValue); 130 } 131 default: 132 { 133 error("Invalid threshold bound {BOUND}", "BOUND", 134 std::to_underlying(bound)); 135 return false; 136 } 137 } 138 } 139 140 void HealthMetric::checkThreshold(ThresholdIntf::Type type, 141 ThresholdIntf::Bound bound, double value) 142 { 143 auto threshold = std::make_tuple(type, bound); 144 auto thresholds = ThresholdIntf::value(); 145 146 if (thresholds.contains(type) && thresholds[type].contains(bound)) 147 { 148 auto thresholdValue = thresholds[type][bound]; 149 auto assertions = ThresholdIntf::asserted(); 150 if (didThresholdViolate(bound, thresholdValue, value)) 151 { 152 if (!assertions.contains(threshold)) 153 { 154 assertions.insert(threshold); 155 ThresholdIntf::asserted(assertions); 156 ThresholdIntf::assertionChanged(type, bound, true, value); 157 auto tConfig = config.thresholds.at(threshold); 158 if (tConfig.log) 159 { 160 error( 161 "ASSERT: Health Metric {METRIC} crossed {TYPE} upper threshold", 162 "METRIC", config.name, "TYPE", 163 sdbusplus::message::convert_to_string(type)); 164 startUnit(bus, tConfig.target); 165 } 166 } 167 return; 168 } 169 else if (assertions.contains(threshold)) 170 { 171 assertions.erase(threshold); 172 ThresholdIntf::asserted(assertions); 173 ThresholdIntf::assertionChanged(type, bound, false, value); 174 if (config.thresholds.find(threshold)->second.log) 175 { 176 info( 177 "DEASSERT: Health Metric {METRIC} is below {TYPE} upper threshold", 178 "METRIC", config.name, "TYPE", 179 sdbusplus::message::convert_to_string(type)); 180 } 181 } 182 } 183 } 184 185 void HealthMetric::checkThresholds(double value) 186 { 187 if (!ThresholdIntf::value().empty()) 188 { 189 for (auto type : 190 {ThresholdIntf::Type::HardShutdown, 191 ThresholdIntf::Type::SoftShutdown, 192 ThresholdIntf::Type::PerformanceLoss, 193 ThresholdIntf::Type::Critical, ThresholdIntf::Type::Warning}) 194 { 195 checkThreshold(type, ThresholdIntf::Bound::Lower, value); 196 checkThreshold(type, ThresholdIntf::Bound::Upper, value); 197 } 198 } 199 } 200 201 void HealthMetric::update(MValue value) 202 { 203 // Maintain window size for metric 204 if (history.size() >= config.windowSize) 205 { 206 history.pop_front(); 207 } 208 history.push_back(value.user); 209 210 if (history.size() < config.windowSize) 211 { 212 // Wait for the metric to have enough samples to calculate average 213 debug("Not enough samples to calculate average"); 214 return; 215 } 216 217 double average = (std::accumulate(history.begin(), history.end(), 0.0)) / 218 history.size(); 219 ValueIntf::value(average); 220 checkThresholds(value.monitor); 221 } 222 223 void HealthMetric::create(const paths_t& bmcPaths) 224 { 225 info("Create Health Metric: {METRIC}", "METRIC", config.name); 226 initProperties(); 227 228 std::vector<association_t> associations; 229 static constexpr auto forwardAssociation = "measuring"; 230 static constexpr auto reverseAssociation = "measured_by"; 231 for (const auto& bmcPath : bmcPaths) 232 { 233 /* 234 * This metric is "measuring" the health for the BMC at bmcPath 235 * The BMC at bmcPath is "measured_by" this metric. 236 */ 237 associations.push_back( 238 {forwardAssociation, reverseAssociation, bmcPath}); 239 } 240 AssociationIntf::associations(associations); 241 } 242 243 } // namespace phosphor::health::metric 244