1 /** 2 * Copyright © 2020 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "json_parser.hpp" 17 18 #include "conditions.hpp" 19 #include "json_config.hpp" 20 #include "nonzero_speed_trust.hpp" 21 #include "power_interface.hpp" 22 #include "power_off_rule.hpp" 23 #include "types.hpp" 24 25 #include <fmt/format.h> 26 27 #include <nlohmann/json.hpp> 28 #include <phosphor-logging/log.hpp> 29 30 #include <algorithm> 31 #include <map> 32 #include <memory> 33 #include <optional> 34 #include <vector> 35 36 namespace phosphor::fan::monitor 37 { 38 39 using json = nlohmann::json; 40 using namespace phosphor::logging; 41 42 namespace tClass 43 { 44 45 // Get a constructed trust group class for a non-zero speed group 46 CreateGroupFunction 47 getNonZeroSpeed(const std::vector<trust::GroupDefinition>& group) 48 { 49 return [group]() { 50 return std::make_unique<trust::NonzeroSpeed>(std::move(group)); 51 }; 52 } 53 54 } // namespace tClass 55 56 const std::map<std::string, trustHandler> trusts = { 57 {"nonzerospeed", tClass::getNonZeroSpeed}}; 58 const std::map<std::string, condHandler> conditions = { 59 {"propertiesmatch", condition::getPropertiesMatch}}; 60 61 const std::vector<CreateGroupFunction> getTrustGrps(const json& obj) 62 { 63 std::vector<CreateGroupFunction> grpFuncs; 64 65 if (obj.contains("sensor_trust_groups")) 66 { 67 for (auto& stg : obj["sensor_trust_groups"]) 68 { 69 if (!stg.contains("class") || !stg.contains("group")) 70 { 71 // Log error on missing required parameters 72 log<level::ERR>( 73 "Missing required fan monitor trust group parameters", 74 entry("REQUIRED_PARAMETERS=%s", "{class, group}")); 75 throw std::runtime_error( 76 "Missing required fan trust group parameters"); 77 } 78 auto tgClass = stg["class"].get<std::string>(); 79 std::vector<trust::GroupDefinition> group; 80 for (auto& member : stg["group"]) 81 { 82 // Construct list of group members 83 if (!member.contains("name")) 84 { 85 // Log error on missing required parameter 86 log<level::ERR>( 87 "Missing required fan monitor trust group member name", 88 entry("CLASS=%s", tgClass.c_str())); 89 throw std::runtime_error( 90 "Missing required fan monitor trust group member name"); 91 } 92 auto in_trust = true; 93 if (member.contains("in_trust")) 94 { 95 in_trust = member["in_trust"].get<bool>(); 96 } 97 group.emplace_back(trust::GroupDefinition{ 98 member["name"].get<std::string>(), in_trust}); 99 } 100 // The class for fan sensor trust groups 101 // (Must have a supported function within the tClass namespace) 102 std::transform(tgClass.begin(), tgClass.end(), tgClass.begin(), 103 tolower); 104 auto handler = trusts.find(tgClass); 105 if (handler != trusts.end()) 106 { 107 // Call function for trust group class 108 grpFuncs.emplace_back(handler->second(group)); 109 } 110 else 111 { 112 // Log error on unsupported trust group class 113 log<level::ERR>("Invalid fan monitor trust group class", 114 entry("CLASS=%s", tgClass.c_str())); 115 throw std::runtime_error( 116 "Invalid fan monitor trust group class"); 117 } 118 } 119 } 120 121 return grpFuncs; 122 } 123 124 const std::vector<SensorDefinition> getSensorDefs(const json& sensors) 125 { 126 std::vector<SensorDefinition> sensorDefs; 127 128 for (const auto& sensor : sensors) 129 { 130 if (!sensor.contains("name") || !sensor.contains("has_target")) 131 { 132 // Log error on missing required parameters 133 log<level::ERR>( 134 "Missing required fan sensor definition parameters", 135 entry("REQUIRED_PARAMETERS=%s", "{name, has_target}")); 136 throw std::runtime_error( 137 "Missing required fan sensor definition parameters"); 138 } 139 // Target interface is optional and defaults to 140 // 'xyz.openbmc_project.Control.FanSpeed' 141 std::string targetIntf = "xyz.openbmc_project.Control.FanSpeed"; 142 if (sensor.contains("target_interface")) 143 { 144 targetIntf = sensor["target_interface"].get<std::string>(); 145 } 146 // Factor is optional and defaults to 1 147 auto factor = 1.0; 148 if (sensor.contains("factor")) 149 { 150 factor = sensor["factor"].get<double>(); 151 } 152 // Offset is optional and defaults to 0 153 auto offset = 0; 154 if (sensor.contains("offset")) 155 { 156 offset = sensor["offset"].get<int64_t>(); 157 } 158 159 sensorDefs.emplace_back(std::tuple(sensor["name"].get<std::string>(), 160 sensor["has_target"].get<bool>(), 161 targetIntf, factor, offset)); 162 } 163 164 return sensorDefs; 165 } 166 167 const std::vector<FanDefinition> getFanDefs(const json& obj) 168 { 169 std::vector<FanDefinition> fanDefs; 170 171 for (const auto& fan : obj["fans"]) 172 { 173 if (!fan.contains("inventory") || 174 !fan.contains("allowed_out_of_range_time") || 175 !fan.contains("deviation") || !fan.contains("sensors")) 176 { 177 // Log error on missing required parameters 178 log<level::ERR>( 179 "Missing required fan monitor definition parameters", 180 entry("REQUIRED_PARAMETERS=%s", 181 "{inventory, allowed_out_of_range_time, deviation, " 182 "sensors}")); 183 throw std::runtime_error( 184 "Missing required fan monitor definition parameters"); 185 } 186 // Construct the sensor definitions for this fan 187 auto sensorDefs = getSensorDefs(fan["sensors"]); 188 189 // Functional delay is optional and defaults to 0 190 size_t funcDelay = 0; 191 if (fan.contains("functional_delay")) 192 { 193 funcDelay = fan["functional_delay"].get<size_t>(); 194 } 195 196 // Monitor start delay is optional and defaults to 0 197 size_t monitorDelay = 0; 198 if (fan.contains("monitor_start_delay")) 199 { 200 monitorDelay = fan["monitor_start_delay"].get<size_t>(); 201 } 202 203 // num_sensors_nonfunc_for_fan_nonfunc is optional and defaults 204 // to zero if not present, meaning the code will not set the 205 // parent fan to nonfunctional based on sensors. 206 size_t nonfuncSensorsCount = 0; 207 if (fan.contains("num_sensors_nonfunc_for_fan_nonfunc")) 208 { 209 nonfuncSensorsCount = 210 fan["num_sensors_nonfunc_for_fan_nonfunc"].get<size_t>(); 211 } 212 213 // nonfunc_rotor_error_delay is optional, though it will 214 // default to zero if 'fault_handling' is present. 215 std::optional<size_t> nonfuncRotorErrorDelay; 216 if (fan.contains("nonfunc_rotor_error_delay")) 217 { 218 nonfuncRotorErrorDelay = 219 fan["nonfunc_rotor_error_delay"].get<size_t>(); 220 } 221 else if (obj.contains("fault_handling")) 222 { 223 nonfuncRotorErrorDelay = 0; 224 } 225 226 // fan_missing_error_delay is optional. 227 std::optional<size_t> fanMissingErrorDelay; 228 if (fan.contains("fan_missing_error_delay")) 229 { 230 fanMissingErrorDelay = 231 fan.at("fan_missing_error_delay").get<size_t>(); 232 } 233 234 // Handle optional conditions 235 auto cond = std::optional<Condition>(); 236 if (fan.contains("condition")) 237 { 238 if (!fan["condition"].contains("name")) 239 { 240 // Log error on missing required parameter 241 log<level::ERR>( 242 "Missing required fan monitor condition parameter", 243 entry("REQUIRED_PARAMETER=%s", "{name}")); 244 throw std::runtime_error( 245 "Missing required fan monitor condition parameter"); 246 } 247 auto name = fan["condition"]["name"].get<std::string>(); 248 // The function for fan monitoring condition 249 // (Must have a supported function within the condition namespace) 250 std::transform(name.begin(), name.end(), name.begin(), tolower); 251 auto handler = conditions.find(name); 252 if (handler != conditions.end()) 253 { 254 cond = handler->second(fan["condition"]); 255 } 256 else 257 { 258 log<level::INFO>( 259 "No handler found for configured condition", 260 entry("CONDITION_NAME=%s", name.c_str()), 261 entry("JSON_DUMP=%s", fan["condition"].dump().c_str())); 262 } 263 } 264 fanDefs.emplace_back(std::tuple( 265 fan["inventory"].get<std::string>(), funcDelay, 266 fan["allowed_out_of_range_time"].get<size_t>(), 267 fan["deviation"].get<size_t>(), nonfuncSensorsCount, monitorDelay, 268 nonfuncRotorErrorDelay, fanMissingErrorDelay, sensorDefs, cond)); 269 } 270 271 return fanDefs; 272 } 273 274 PowerRuleState getPowerOffPowerRuleState(const json& powerOffConfig) 275 { 276 // The state is optional and defaults to runtime 277 PowerRuleState ruleState{PowerRuleState::runtime}; 278 279 if (powerOffConfig.contains("state")) 280 { 281 auto state = powerOffConfig.at("state").get<std::string>(); 282 if (state == "at_pgood") 283 { 284 ruleState = PowerRuleState::atPgood; 285 } 286 else if (state != "runtime") 287 { 288 auto msg = fmt::format("Invalid power off state entry {}", state); 289 log<level::ERR>(msg.c_str()); 290 throw std::runtime_error(msg.c_str()); 291 } 292 } 293 294 return ruleState; 295 } 296 297 std::unique_ptr<PowerOffCause> getPowerOffCause(const json& powerOffConfig) 298 { 299 std::unique_ptr<PowerOffCause> cause; 300 301 if (!powerOffConfig.contains("count") || !powerOffConfig.contains("cause")) 302 { 303 const auto msg = 304 "Missing 'count' or 'cause' entries in power off config"; 305 log<level::ERR>(msg); 306 throw std::runtime_error(msg); 307 } 308 309 auto count = powerOffConfig.at("count").get<size_t>(); 310 auto powerOffCause = powerOffConfig.at("cause").get<std::string>(); 311 312 const std::map<std::string, std::function<std::unique_ptr<PowerOffCause>()>> 313 causes{ 314 {"missing_fan_frus", 315 [count]() { return std::make_unique<MissingFanFRUCause>(count); }}, 316 {"nonfunc_fan_rotors", [count]() { 317 return std::make_unique<NonfuncFanRotorCause>(count); 318 }}}; 319 320 auto it = causes.find(powerOffCause); 321 if (it != causes.end()) 322 { 323 cause = it->second(); 324 } 325 else 326 { 327 auto msg = 328 fmt::format("Invalid power off cause {} in power off config JSON", 329 powerOffCause); 330 log<level::ERR>(msg.c_str()); 331 throw std::runtime_error(msg.c_str()); 332 } 333 334 return cause; 335 } 336 337 std::unique_ptr<PowerOffAction> 338 getPowerOffAction(const json& powerOffConfig, 339 std::shared_ptr<PowerInterfaceBase>& powerInterface, 340 PowerOffAction::PrePowerOffFunc& func) 341 { 342 std::unique_ptr<PowerOffAction> action; 343 if (!powerOffConfig.contains("type")) 344 { 345 const auto msg = "Missing 'type' entry in power off config"; 346 log<level::ERR>(msg); 347 throw std::runtime_error(msg); 348 } 349 350 auto type = powerOffConfig.at("type").get<std::string>(); 351 352 if (((type == "hard") || (type == "soft")) && 353 !powerOffConfig.contains("delay")) 354 { 355 const auto msg = "Missing 'delay' entry in power off config"; 356 log<level::ERR>(msg); 357 throw std::runtime_error(msg); 358 } 359 else if ((type == "epow") && 360 (!powerOffConfig.contains("service_mode_delay") || 361 !powerOffConfig.contains("meltdown_delay"))) 362 { 363 const auto msg = "Missing 'service_mode_delay' or 'meltdown_delay' " 364 "entry in power off config"; 365 log<level::ERR>(msg); 366 throw std::runtime_error(msg); 367 } 368 369 if (type == "hard") 370 { 371 action = std::make_unique<HardPowerOff>( 372 powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func); 373 } 374 else if (type == "soft") 375 { 376 action = std::make_unique<SoftPowerOff>( 377 powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func); 378 } 379 else if (type == "epow") 380 { 381 action = std::make_unique<EpowPowerOff>( 382 powerOffConfig.at("service_mode_delay").get<uint32_t>(), 383 powerOffConfig.at("meltdown_delay").get<uint32_t>(), powerInterface, 384 func); 385 } 386 else 387 { 388 auto msg = 389 fmt::format("Invalid 'type' entry {} in power off config", type); 390 log<level::ERR>(msg.c_str()); 391 throw std::runtime_error(msg.c_str()); 392 } 393 394 return action; 395 } 396 397 std::vector<std::unique_ptr<PowerOffRule>> 398 getPowerOffRules(const json& obj, 399 std::shared_ptr<PowerInterfaceBase>& powerInterface, 400 PowerOffAction::PrePowerOffFunc& func) 401 { 402 std::vector<std::unique_ptr<PowerOffRule>> rules; 403 404 if (!(obj.contains("fault_handling") && 405 obj.at("fault_handling").contains("power_off_config"))) 406 { 407 return rules; 408 } 409 410 for (const auto& config : obj.at("fault_handling").at("power_off_config")) 411 { 412 auto state = getPowerOffPowerRuleState(config); 413 auto cause = getPowerOffCause(config); 414 auto action = getPowerOffAction(config, powerInterface, func); 415 416 auto rule = std::make_unique<PowerOffRule>( 417 std::move(state), std::move(cause), std::move(action)); 418 rules.push_back(std::move(rule)); 419 } 420 421 return rules; 422 } 423 424 std::optional<size_t> getNumNonfuncRotorsBeforeError(const json& obj) 425 { 426 std::optional<size_t> num; 427 428 if (obj.contains("fault_handling")) 429 { 430 // Defaults to 1 if not present inside of 'fault_handling'. 431 num = obj.at("fault_handling") 432 .value("num_nonfunc_rotors_before_error", 1); 433 } 434 435 return num; 436 } 437 438 } // namespace phosphor::fan::monitor 439