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