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 "tach_sensor.hpp" 24 #include "types.hpp" 25 26 #include <nlohmann/json.hpp> 27 #include <phosphor-logging/log.hpp> 28 29 #include <algorithm> 30 #include <format> 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 const std::map<std::string, size_t> methods = { 61 {"timebased", MethodMode::timebased}, {"count", MethodMode::count}}; 62 63 const std::vector<CreateGroupFunction> getTrustGrps(const json& obj) 64 { 65 std::vector<CreateGroupFunction> grpFuncs; 66 67 if (obj.contains("sensor_trust_groups")) 68 { 69 for (auto& stg : obj["sensor_trust_groups"]) 70 { 71 if (!stg.contains("class") || !stg.contains("group")) 72 { 73 // Log error on missing required parameters 74 log<level::ERR>( 75 "Missing required fan monitor trust group parameters", 76 entry("REQUIRED_PARAMETERS=%s", "{class, group}")); 77 throw std::runtime_error( 78 "Missing required fan trust group parameters"); 79 } 80 auto tgClass = stg["class"].get<std::string>(); 81 std::vector<trust::GroupDefinition> group; 82 for (auto& member : stg["group"]) 83 { 84 // Construct list of group members 85 if (!member.contains("name")) 86 { 87 // Log error on missing required parameter 88 log<level::ERR>( 89 "Missing required fan monitor trust group member name", 90 entry("CLASS=%s", tgClass.c_str())); 91 throw std::runtime_error( 92 "Missing required fan monitor trust group member name"); 93 } 94 auto in_trust = true; 95 if (member.contains("in_trust")) 96 { 97 in_trust = member["in_trust"].get<bool>(); 98 } 99 group.emplace_back(trust::GroupDefinition{ 100 member["name"].get<std::string>(), in_trust}); 101 } 102 // The class for fan sensor trust groups 103 // (Must have a supported function within the tClass namespace) 104 std::transform(tgClass.begin(), tgClass.end(), tgClass.begin(), 105 tolower); 106 auto handler = trusts.find(tgClass); 107 if (handler != trusts.end()) 108 { 109 // Call function for trust group class 110 grpFuncs.emplace_back(handler->second(group)); 111 } 112 else 113 { 114 // Log error on unsupported trust group class 115 log<level::ERR>("Invalid fan monitor trust group class", 116 entry("CLASS=%s", tgClass.c_str())); 117 throw std::runtime_error( 118 "Invalid fan monitor trust group class"); 119 } 120 } 121 } 122 123 return grpFuncs; 124 } 125 126 const std::vector<SensorDefinition> getSensorDefs(const json& sensors) 127 { 128 std::vector<SensorDefinition> sensorDefs; 129 130 for (const auto& sensor : sensors) 131 { 132 if (!sensor.contains("name") || !sensor.contains("has_target")) 133 { 134 // Log error on missing required parameters 135 log<level::ERR>( 136 "Missing required fan sensor definition parameters", 137 entry("REQUIRED_PARAMETERS=%s", "{name, has_target}")); 138 throw std::runtime_error( 139 "Missing required fan sensor definition parameters"); 140 } 141 // Target interface is optional and defaults to 142 // 'xyz.openbmc_project.Control.FanSpeed' 143 std::string targetIntf = "xyz.openbmc_project.Control.FanSpeed"; 144 if (sensor.contains("target_interface")) 145 { 146 targetIntf = sensor["target_interface"].get<std::string>(); 147 } 148 // Target path is optional 149 std::string targetPath; 150 if (sensor.contains("target_path")) 151 { 152 targetPath = sensor["target_path"].get<std::string>(); 153 } 154 // Factor is optional and defaults to 1 155 double factor = 1.0; 156 if (sensor.contains("factor")) 157 { 158 factor = sensor["factor"].get<double>(); 159 } 160 // Offset is optional and defaults to 0 161 int64_t offset = 0; 162 if (sensor.contains("offset")) 163 { 164 offset = sensor["offset"].get<int64_t>(); 165 } 166 // Threshold is optional and defaults to 1 167 size_t threshold = 1; 168 if (sensor.contains("threshold")) 169 { 170 threshold = sensor["threshold"].get<size_t>(); 171 } 172 // Ignore being above the allowed max is optional, defaults to not 173 bool ignoreAboveMax = false; 174 if (sensor.contains("ignore_above_max")) 175 { 176 ignoreAboveMax = sensor["ignore_above_max"].get<bool>(); 177 } 178 179 SensorDefinition def{.name = sensor["name"].get<std::string>(), 180 .hasTarget = sensor["has_target"].get<bool>(), 181 .targetInterface = targetIntf, 182 .targetPath = targetPath, 183 .factor = factor, 184 .offset = offset, 185 .threshold = threshold, 186 .ignoreAboveMax = ignoreAboveMax}; 187 188 sensorDefs.push_back(std::move(def)); 189 } 190 191 return sensorDefs; 192 } 193 194 const std::vector<FanDefinition> getFanDefs(const json& obj) 195 { 196 std::vector<FanDefinition> fanDefs; 197 198 for (const auto& fan : obj["fans"]) 199 { 200 if (!fan.contains("inventory") || !fan.contains("deviation") || 201 !fan.contains("sensors")) 202 { 203 // Log error on missing required parameters 204 log<level::ERR>( 205 "Missing required fan monitor definition parameters", 206 entry("REQUIRED_PARAMETERS=%s", 207 "{inventory, deviation, sensors}")); 208 throw std::runtime_error( 209 "Missing required fan monitor definition parameters"); 210 } 211 // Valid deviation range is 0 - 100% 212 auto deviation = fan["deviation"].get<size_t>(); 213 if (100 < deviation) 214 { 215 auto msg = std::format( 216 "Invalid deviation of {} found, must be between 0 and 100", 217 deviation); 218 219 log<level::ERR>(msg.c_str()); 220 throw std::runtime_error(msg.c_str()); 221 } 222 223 // Upper deviation defaults to the deviation value and 224 // can also be separately specified. 225 size_t upperDeviation = deviation; 226 if (fan.contains("upper_deviation")) 227 { 228 upperDeviation = fan["upper_deviation"].get<size_t>(); 229 if (100 < upperDeviation) 230 { 231 auto msg = 232 std::format("Invalid upper_deviation of {} found, must " 233 "be between 0 and 100", 234 upperDeviation); 235 236 log<level::ERR>(msg.c_str()); 237 throw std::runtime_error(msg.c_str()); 238 } 239 } 240 241 // Construct the sensor definitions for this fan 242 auto sensorDefs = getSensorDefs(fan["sensors"]); 243 244 // Functional delay is optional and defaults to 0 245 size_t funcDelay = 0; 246 if (fan.contains("functional_delay")) 247 { 248 funcDelay = fan["functional_delay"].get<size_t>(); 249 } 250 251 // Method is optional and defaults to time based functional 252 // determination 253 size_t method = MethodMode::timebased; 254 size_t countInterval = 1; 255 if (fan.contains("method")) 256 { 257 auto methodConf = fan["method"].get<std::string>(); 258 auto methodFunc = methods.find(methodConf); 259 if (methodFunc != methods.end()) 260 { 261 method = methodFunc->second; 262 } 263 else 264 { 265 // Log error on unsupported method parameter 266 log<level::ERR>("Invalid fan method"); 267 throw std::runtime_error("Invalid fan method"); 268 } 269 270 // Read the count interval value used with the count method. 271 if (method == MethodMode::count) 272 { 273 if (fan.contains("count_interval")) 274 { 275 countInterval = fan["count_interval"].get<size_t>(); 276 } 277 } 278 } 279 280 // Timeout defaults to 0 281 size_t timeout = 0; 282 if (method == MethodMode::timebased) 283 { 284 if (!fan.contains("allowed_out_of_range_time")) 285 { 286 // Log error on missing required parameter 287 log<level::ERR>( 288 "Missing required fan monitor definition parameters", 289 entry("REQUIRED_PARAMETER=%s", 290 "{allowed_out_of_range_time}")); 291 throw std::runtime_error( 292 "Missing required fan monitor definition parameters"); 293 } 294 else 295 { 296 timeout = fan["allowed_out_of_range_time"].get<size_t>(); 297 } 298 } 299 300 // Monitor start delay is optional and defaults to 0 301 size_t monitorDelay = 0; 302 if (fan.contains("monitor_start_delay")) 303 { 304 monitorDelay = fan["monitor_start_delay"].get<size_t>(); 305 } 306 307 // num_sensors_nonfunc_for_fan_nonfunc is optional and defaults 308 // to zero if not present, meaning the code will not set the 309 // parent fan to nonfunctional based on sensors. 310 size_t nonfuncSensorsCount = 0; 311 if (fan.contains("num_sensors_nonfunc_for_fan_nonfunc")) 312 { 313 nonfuncSensorsCount = 314 fan["num_sensors_nonfunc_for_fan_nonfunc"].get<size_t>(); 315 } 316 317 // nonfunc_rotor_error_delay is optional, though it will 318 // default to zero if 'fault_handling' is present. 319 std::optional<size_t> nonfuncRotorErrorDelay; 320 if (fan.contains("nonfunc_rotor_error_delay")) 321 { 322 nonfuncRotorErrorDelay = 323 fan["nonfunc_rotor_error_delay"].get<size_t>(); 324 } 325 else if (obj.contains("fault_handling")) 326 { 327 nonfuncRotorErrorDelay = 0; 328 } 329 330 // fan_missing_error_delay is optional. 331 std::optional<size_t> fanMissingErrorDelay; 332 if (fan.contains("fan_missing_error_delay")) 333 { 334 fanMissingErrorDelay = 335 fan.at("fan_missing_error_delay").get<size_t>(); 336 } 337 338 // Handle optional conditions 339 auto cond = std::optional<Condition>(); 340 if (fan.contains("condition")) 341 { 342 if (!fan["condition"].contains("name")) 343 { 344 // Log error on missing required parameter 345 log<level::ERR>( 346 "Missing required fan monitor condition parameter", 347 entry("REQUIRED_PARAMETER=%s", "{name}")); 348 throw std::runtime_error( 349 "Missing required fan monitor condition parameter"); 350 } 351 auto name = fan["condition"]["name"].get<std::string>(); 352 // The function for fan monitoring condition 353 // (Must have a supported function within the condition namespace) 354 std::transform(name.begin(), name.end(), name.begin(), tolower); 355 auto handler = conditions.find(name); 356 if (handler != conditions.end()) 357 { 358 cond = handler->second(fan["condition"]); 359 } 360 else 361 { 362 log<level::INFO>( 363 "No handler found for configured condition", 364 entry("CONDITION_NAME=%s", name.c_str()), 365 entry("JSON_DUMP=%s", fan["condition"].dump().c_str())); 366 } 367 } 368 369 // if the fan should be set to functional when plugged in 370 bool setFuncOnPresent = false; 371 if (fan.contains("set_func_on_present")) 372 { 373 setFuncOnPresent = fan["set_func_on_present"].get<bool>(); 374 } 375 376 FanDefinition def{.name = fan["inventory"].get<std::string>(), 377 .method = method, 378 .funcDelay = funcDelay, 379 .timeout = timeout, 380 .deviation = deviation, 381 .upperDeviation = upperDeviation, 382 .numSensorFailsForNonfunc = nonfuncSensorsCount, 383 .monitorStartDelay = monitorDelay, 384 .countInterval = countInterval, 385 .nonfuncRotorErrDelay = nonfuncRotorErrorDelay, 386 .fanMissingErrDelay = fanMissingErrorDelay, 387 .sensorList = std::move(sensorDefs), 388 .condition = cond, 389 .funcOnPresent = setFuncOnPresent}; 390 391 fanDefs.push_back(std::move(def)); 392 } 393 394 return fanDefs; 395 } 396 397 PowerRuleState getPowerOffPowerRuleState(const json& powerOffConfig) 398 { 399 // The state is optional and defaults to runtime 400 PowerRuleState ruleState{PowerRuleState::runtime}; 401 402 if (powerOffConfig.contains("state")) 403 { 404 auto state = powerOffConfig.at("state").get<std::string>(); 405 if (state == "at_pgood") 406 { 407 ruleState = PowerRuleState::atPgood; 408 } 409 else if (state != "runtime") 410 { 411 auto msg = std::format("Invalid power off state entry {}", state); 412 log<level::ERR>(msg.c_str()); 413 throw std::runtime_error(msg.c_str()); 414 } 415 } 416 417 return ruleState; 418 } 419 420 std::unique_ptr<PowerOffCause> getPowerOffCause(const json& powerOffConfig) 421 { 422 std::unique_ptr<PowerOffCause> cause; 423 424 if (!powerOffConfig.contains("count") || !powerOffConfig.contains("cause")) 425 { 426 const auto msg = 427 "Missing 'count' or 'cause' entries in power off config"; 428 log<level::ERR>(msg); 429 throw std::runtime_error(msg); 430 } 431 432 auto count = powerOffConfig.at("count").get<size_t>(); 433 auto powerOffCause = powerOffConfig.at("cause").get<std::string>(); 434 435 const std::map<std::string, std::function<std::unique_ptr<PowerOffCause>()>> 436 causes{ 437 {"missing_fan_frus", 438 [count]() { return std::make_unique<MissingFanFRUCause>(count); }}, 439 {"nonfunc_fan_rotors", [count]() { 440 return std::make_unique<NonfuncFanRotorCause>(count); 441 }}}; 442 443 auto it = causes.find(powerOffCause); 444 if (it != causes.end()) 445 { 446 cause = it->second(); 447 } 448 else 449 { 450 auto msg = 451 std::format("Invalid power off cause {} in power off config JSON", 452 powerOffCause); 453 log<level::ERR>(msg.c_str()); 454 throw std::runtime_error(msg.c_str()); 455 } 456 457 return cause; 458 } 459 460 std::unique_ptr<PowerOffAction> 461 getPowerOffAction(const json& powerOffConfig, 462 std::shared_ptr<PowerInterfaceBase>& powerInterface, 463 PowerOffAction::PrePowerOffFunc& func) 464 { 465 std::unique_ptr<PowerOffAction> action; 466 if (!powerOffConfig.contains("type")) 467 { 468 const auto msg = "Missing 'type' entry in power off config"; 469 log<level::ERR>(msg); 470 throw std::runtime_error(msg); 471 } 472 473 auto type = powerOffConfig.at("type").get<std::string>(); 474 475 if (((type == "hard") || (type == "soft")) && 476 !powerOffConfig.contains("delay")) 477 { 478 const auto msg = "Missing 'delay' entry in power off config"; 479 log<level::ERR>(msg); 480 throw std::runtime_error(msg); 481 } 482 else if ((type == "epow") && 483 (!powerOffConfig.contains("service_mode_delay") || 484 !powerOffConfig.contains("meltdown_delay"))) 485 { 486 const auto msg = "Missing 'service_mode_delay' or 'meltdown_delay' " 487 "entry in power off config"; 488 log<level::ERR>(msg); 489 throw std::runtime_error(msg); 490 } 491 492 if (type == "hard") 493 { 494 action = std::make_unique<HardPowerOff>( 495 powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func); 496 } 497 else if (type == "soft") 498 { 499 action = std::make_unique<SoftPowerOff>( 500 powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func); 501 } 502 else if (type == "epow") 503 { 504 action = std::make_unique<EpowPowerOff>( 505 powerOffConfig.at("service_mode_delay").get<uint32_t>(), 506 powerOffConfig.at("meltdown_delay").get<uint32_t>(), powerInterface, 507 func); 508 } 509 else 510 { 511 auto msg = std::format("Invalid 'type' entry {} in power off config", 512 type); 513 log<level::ERR>(msg.c_str()); 514 throw std::runtime_error(msg.c_str()); 515 } 516 517 return action; 518 } 519 520 std::vector<std::unique_ptr<PowerOffRule>> 521 getPowerOffRules(const json& obj, 522 std::shared_ptr<PowerInterfaceBase>& powerInterface, 523 PowerOffAction::PrePowerOffFunc& func) 524 { 525 std::vector<std::unique_ptr<PowerOffRule>> rules; 526 527 if (!(obj.contains("fault_handling") && 528 obj.at("fault_handling").contains("power_off_config"))) 529 { 530 return rules; 531 } 532 533 for (const auto& config : obj.at("fault_handling").at("power_off_config")) 534 { 535 auto state = getPowerOffPowerRuleState(config); 536 auto cause = getPowerOffCause(config); 537 auto action = getPowerOffAction(config, powerInterface, func); 538 539 auto rule = std::make_unique<PowerOffRule>( 540 std::move(state), std::move(cause), std::move(action)); 541 rules.push_back(std::move(rule)); 542 } 543 544 return rules; 545 } 546 547 std::optional<size_t> getNumNonfuncRotorsBeforeError(const json& obj) 548 { 549 std::optional<size_t> num; 550 551 if (obj.contains("fault_handling")) 552 { 553 // Defaults to 1 if not present inside of 'fault_handling'. 554 num = obj.at("fault_handling") 555 .value("num_nonfunc_rotors_before_error", 1); 556 } 557 558 return num; 559 } 560 561 } // namespace phosphor::fan::monitor 562