1 /* 2 // Copyright (c) 2018 Intel 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 #pragma once 17 18 #include "bmcweb_config.h" 19 20 #include "app.hpp" 21 #include "dbus_utility.hpp" 22 #include "health.hpp" 23 #include "query.hpp" 24 #include "redfish_util.hpp" 25 #include "registries/privilege_registry.hpp" 26 #include "utils/dbus_utils.hpp" 27 #include "utils/json_utils.hpp" 28 #include "utils/sw_utils.hpp" 29 #include "utils/systemd_utils.hpp" 30 #include "utils/time_utils.hpp" 31 32 #include <boost/system/error_code.hpp> 33 #include <boost/url/format.hpp> 34 #include <sdbusplus/asio/property.hpp> 35 #include <sdbusplus/unpack_properties.hpp> 36 37 #include <algorithm> 38 #include <array> 39 #include <cstdint> 40 #include <memory> 41 #include <ranges> 42 #include <sstream> 43 #include <string_view> 44 #include <variant> 45 46 namespace redfish 47 { 48 49 /** 50 * Function reboots the BMC. 51 * 52 * @param[in] asyncResp - Shared pointer for completing asynchronous calls 53 */ 54 inline void 55 doBMCGracefulRestart(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 56 { 57 const char* processName = "xyz.openbmc_project.State.BMC"; 58 const char* objectPath = "/xyz/openbmc_project/state/bmc0"; 59 const char* interfaceName = "xyz.openbmc_project.State.BMC"; 60 const std::string& propertyValue = 61 "xyz.openbmc_project.State.BMC.Transition.Reboot"; 62 const char* destProperty = "RequestedBMCTransition"; 63 64 // Create the D-Bus variant for D-Bus call. 65 sdbusplus::asio::setProperty( 66 *crow::connections::systemBus, processName, objectPath, interfaceName, 67 destProperty, propertyValue, 68 [asyncResp](const boost::system::error_code& ec) { 69 // Use "Set" method to set the property value. 70 if (ec) 71 { 72 BMCWEB_LOG_DEBUG("[Set] Bad D-Bus request error: {}", ec); 73 messages::internalError(asyncResp->res); 74 return; 75 } 76 77 messages::success(asyncResp->res); 78 }); 79 } 80 81 inline void 82 doBMCForceRestart(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 83 { 84 const char* processName = "xyz.openbmc_project.State.BMC"; 85 const char* objectPath = "/xyz/openbmc_project/state/bmc0"; 86 const char* interfaceName = "xyz.openbmc_project.State.BMC"; 87 const std::string& propertyValue = 88 "xyz.openbmc_project.State.BMC.Transition.HardReboot"; 89 const char* destProperty = "RequestedBMCTransition"; 90 91 // Create the D-Bus variant for D-Bus call. 92 sdbusplus::asio::setProperty( 93 *crow::connections::systemBus, processName, objectPath, interfaceName, 94 destProperty, propertyValue, 95 [asyncResp](const boost::system::error_code& ec) { 96 // Use "Set" method to set the property value. 97 if (ec) 98 { 99 BMCWEB_LOG_DEBUG("[Set] Bad D-Bus request error: {}", ec); 100 messages::internalError(asyncResp->res); 101 return; 102 } 103 104 messages::success(asyncResp->res); 105 }); 106 } 107 108 /** 109 * ManagerResetAction class supports the POST method for the Reset (reboot) 110 * action. 111 */ 112 inline void requestRoutesManagerResetAction(App& app) 113 { 114 /** 115 * Function handles POST method request. 116 * Analyzes POST body before sending Reset (Reboot) request data to D-Bus. 117 * OpenBMC supports ResetType "GracefulRestart" and "ForceRestart". 118 */ 119 120 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Actions/Manager.Reset/") 121 .privileges(redfish::privileges::postManager) 122 .methods(boost::beast::http::verb::post)( 123 [&app](const crow::Request& req, 124 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 125 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 126 { 127 return; 128 } 129 BMCWEB_LOG_DEBUG("Post Manager Reset."); 130 131 std::string resetType; 132 133 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", 134 resetType)) 135 { 136 return; 137 } 138 139 if (resetType == "GracefulRestart") 140 { 141 BMCWEB_LOG_DEBUG("Proceeding with {}", resetType); 142 doBMCGracefulRestart(asyncResp); 143 return; 144 } 145 if (resetType == "ForceRestart") 146 { 147 BMCWEB_LOG_DEBUG("Proceeding with {}", resetType); 148 doBMCForceRestart(asyncResp); 149 return; 150 } 151 BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", resetType); 152 messages::actionParameterNotSupported(asyncResp->res, resetType, 153 "ResetType"); 154 155 return; 156 }); 157 } 158 159 /** 160 * ManagerResetToDefaultsAction class supports POST method for factory reset 161 * action. 162 */ 163 inline void requestRoutesManagerResetToDefaultsAction(App& app) 164 { 165 /** 166 * Function handles ResetToDefaults POST method request. 167 * 168 * Analyzes POST body message and factory resets BMC by calling 169 * BMC code updater factory reset followed by a BMC reboot. 170 * 171 * BMC code updater factory reset wipes the whole BMC read-write 172 * filesystem which includes things like the network settings. 173 * 174 * OpenBMC only supports ResetToDefaultsType "ResetAll". 175 */ 176 177 BMCWEB_ROUTE(app, 178 "/redfish/v1/Managers/bmc/Actions/Manager.ResetToDefaults/") 179 .privileges(redfish::privileges::postManager) 180 .methods(boost::beast::http::verb::post)( 181 [&app](const crow::Request& req, 182 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 183 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 184 { 185 return; 186 } 187 BMCWEB_LOG_DEBUG("Post ResetToDefaults."); 188 189 std::string resetType; 190 191 if (!json_util::readJsonAction(req, asyncResp->res, 192 "ResetToDefaultsType", resetType)) 193 { 194 BMCWEB_LOG_DEBUG("Missing property ResetToDefaultsType."); 195 196 messages::actionParameterMissing(asyncResp->res, "ResetToDefaults", 197 "ResetToDefaultsType"); 198 return; 199 } 200 201 if (resetType != "ResetAll") 202 { 203 BMCWEB_LOG_DEBUG( 204 "Invalid property value for ResetToDefaultsType: {}", 205 resetType); 206 messages::actionParameterNotSupported(asyncResp->res, resetType, 207 "ResetToDefaultsType"); 208 return; 209 } 210 211 crow::connections::systemBus->async_method_call( 212 [asyncResp](const boost::system::error_code& ec) { 213 if (ec) 214 { 215 BMCWEB_LOG_DEBUG("Failed to ResetToDefaults: {}", ec); 216 messages::internalError(asyncResp->res); 217 return; 218 } 219 // Factory Reset doesn't actually happen until a reboot 220 // Can't erase what the BMC is running on 221 doBMCGracefulRestart(asyncResp); 222 }, 223 "xyz.openbmc_project.Software.BMC.Updater", 224 "/xyz/openbmc_project/software", 225 "xyz.openbmc_project.Common.FactoryReset", "Reset"); 226 }); 227 } 228 229 /** 230 * ManagerResetActionInfo derived class for delivering Manager 231 * ResetType AllowableValues using ResetInfo schema. 232 */ 233 inline void requestRoutesManagerResetActionInfo(App& app) 234 { 235 /** 236 * Functions triggers appropriate requests on DBus 237 */ 238 239 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/ResetActionInfo/") 240 .privileges(redfish::privileges::getActionInfo) 241 .methods(boost::beast::http::verb::get)( 242 [&app](const crow::Request& req, 243 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 244 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 245 { 246 return; 247 } 248 249 asyncResp->res.jsonValue["@odata.type"] = 250 "#ActionInfo.v1_1_2.ActionInfo"; 251 asyncResp->res.jsonValue["@odata.id"] = 252 "/redfish/v1/Managers/bmc/ResetActionInfo"; 253 asyncResp->res.jsonValue["Name"] = "Reset Action Info"; 254 asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; 255 nlohmann::json::object_t parameter; 256 parameter["Name"] = "ResetType"; 257 parameter["Required"] = true; 258 parameter["DataType"] = "String"; 259 260 nlohmann::json::array_t allowableValues; 261 allowableValues.emplace_back("GracefulRestart"); 262 allowableValues.emplace_back("ForceRestart"); 263 parameter["AllowableValues"] = std::move(allowableValues); 264 265 nlohmann::json::array_t parameters; 266 parameters.emplace_back(std::move(parameter)); 267 268 asyncResp->res.jsonValue["Parameters"] = std::move(parameters); 269 }); 270 } 271 272 static constexpr const char* objectManagerIface = 273 "org.freedesktop.DBus.ObjectManager"; 274 static constexpr const char* pidConfigurationIface = 275 "xyz.openbmc_project.Configuration.Pid"; 276 static constexpr const char* pidZoneConfigurationIface = 277 "xyz.openbmc_project.Configuration.Pid.Zone"; 278 static constexpr const char* stepwiseConfigurationIface = 279 "xyz.openbmc_project.Configuration.Stepwise"; 280 static constexpr const char* thermalModeIface = 281 "xyz.openbmc_project.Control.ThermalMode"; 282 283 inline void 284 asyncPopulatePid(const std::string& connection, const std::string& path, 285 const std::string& currentProfile, 286 const std::vector<std::string>& supportedProfiles, 287 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 288 { 289 sdbusplus::message::object_path objPath(path); 290 dbus::utility::getManagedObjects( 291 connection, objPath, 292 [asyncResp, currentProfile, supportedProfiles]( 293 const boost::system::error_code& ec, 294 const dbus::utility::ManagedObjectType& managedObj) { 295 if (ec) 296 { 297 BMCWEB_LOG_ERROR("{}", ec); 298 messages::internalError(asyncResp->res); 299 return; 300 } 301 nlohmann::json& configRoot = 302 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"]; 303 nlohmann::json& fans = configRoot["FanControllers"]; 304 fans["@odata.type"] = "#OemManager.FanControllers"; 305 fans["@odata.id"] = 306 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanControllers"; 307 308 nlohmann::json& pids = configRoot["PidControllers"]; 309 pids["@odata.type"] = "#OemManager.PidControllers"; 310 pids["@odata.id"] = 311 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers"; 312 313 nlohmann::json& stepwise = configRoot["StepwiseControllers"]; 314 stepwise["@odata.type"] = "#OemManager.StepwiseControllers"; 315 stepwise["@odata.id"] = 316 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/StepwiseControllers"; 317 318 nlohmann::json& zones = configRoot["FanZones"]; 319 zones["@odata.id"] = 320 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones"; 321 zones["@odata.type"] = "#OemManager.FanZones"; 322 configRoot["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan"; 323 configRoot["@odata.type"] = "#OemManager.Fan"; 324 configRoot["Profile@Redfish.AllowableValues"] = supportedProfiles; 325 326 if (!currentProfile.empty()) 327 { 328 configRoot["Profile"] = currentProfile; 329 } 330 BMCWEB_LOG_DEBUG("profile = {} !", currentProfile); 331 332 for (const auto& pathPair : managedObj) 333 { 334 for (const auto& intfPair : pathPair.second) 335 { 336 if (intfPair.first != pidConfigurationIface && 337 intfPair.first != pidZoneConfigurationIface && 338 intfPair.first != stepwiseConfigurationIface) 339 { 340 continue; 341 } 342 343 std::string name; 344 345 for (const std::pair<std::string, 346 dbus::utility::DbusVariantType>& propPair : 347 intfPair.second) 348 { 349 if (propPair.first == "Name") 350 { 351 const std::string* namePtr = 352 std::get_if<std::string>(&propPair.second); 353 if (namePtr == nullptr) 354 { 355 BMCWEB_LOG_ERROR("Pid Name Field illegal"); 356 messages::internalError(asyncResp->res); 357 return; 358 } 359 name = *namePtr; 360 dbus::utility::escapePathForDbus(name); 361 } 362 else if (propPair.first == "Profiles") 363 { 364 const std::vector<std::string>* profiles = 365 std::get_if<std::vector<std::string>>( 366 &propPair.second); 367 if (profiles == nullptr) 368 { 369 BMCWEB_LOG_ERROR("Pid Profiles Field illegal"); 370 messages::internalError(asyncResp->res); 371 return; 372 } 373 if (std::find(profiles->begin(), profiles->end(), 374 currentProfile) == profiles->end()) 375 { 376 BMCWEB_LOG_INFO( 377 "{} not supported in current profile", name); 378 continue; 379 } 380 } 381 } 382 nlohmann::json* config = nullptr; 383 const std::string* classPtr = nullptr; 384 385 for (const std::pair<std::string, 386 dbus::utility::DbusVariantType>& propPair : 387 intfPair.second) 388 { 389 if (propPair.first == "Class") 390 { 391 classPtr = std::get_if<std::string>(&propPair.second); 392 } 393 } 394 395 boost::urls::url url("/redfish/v1/Managers/bmc"); 396 if (intfPair.first == pidZoneConfigurationIface) 397 { 398 std::string chassis; 399 if (!dbus::utility::getNthStringFromPath(pathPair.first.str, 400 5, chassis)) 401 { 402 chassis = "#IllegalValue"; 403 } 404 nlohmann::json& zone = zones[name]; 405 zone["Chassis"]["@odata.id"] = 406 boost::urls::format("/redfish/v1/Chassis/{}", chassis); 407 url.set_fragment( 408 ("/Oem/OpenBmc/Fan/FanZones"_json_pointer / name) 409 .to_string()); 410 zone["@odata.id"] = std::move(url); 411 zone["@odata.type"] = "#OemManager.FanZone"; 412 config = &zone; 413 } 414 415 else if (intfPair.first == stepwiseConfigurationIface) 416 { 417 if (classPtr == nullptr) 418 { 419 BMCWEB_LOG_ERROR("Pid Class Field illegal"); 420 messages::internalError(asyncResp->res); 421 return; 422 } 423 424 nlohmann::json& controller = stepwise[name]; 425 config = &controller; 426 url.set_fragment( 427 ("/Oem/OpenBmc/Fan/StepwiseControllers"_json_pointer / 428 name) 429 .to_string()); 430 controller["@odata.id"] = std::move(url); 431 controller["@odata.type"] = 432 "#OemManager.StepwiseController"; 433 434 controller["Direction"] = *classPtr; 435 } 436 437 // pid and fans are off the same configuration 438 else if (intfPair.first == pidConfigurationIface) 439 { 440 if (classPtr == nullptr) 441 { 442 BMCWEB_LOG_ERROR("Pid Class Field illegal"); 443 messages::internalError(asyncResp->res); 444 return; 445 } 446 bool isFan = *classPtr == "fan"; 447 nlohmann::json& element = isFan ? fans[name] : pids[name]; 448 config = &element; 449 if (isFan) 450 { 451 url.set_fragment( 452 ("/Oem/OpenBmc/Fan/FanControllers"_json_pointer / 453 name) 454 .to_string()); 455 element["@odata.id"] = std::move(url); 456 element["@odata.type"] = "#OemManager.FanController"; 457 } 458 else 459 { 460 url.set_fragment( 461 ("/Oem/OpenBmc/Fan/PidControllers"_json_pointer / 462 name) 463 .to_string()); 464 element["@odata.id"] = std::move(url); 465 element["@odata.type"] = "#OemManager.PidController"; 466 } 467 } 468 else 469 { 470 BMCWEB_LOG_ERROR("Unexpected configuration"); 471 messages::internalError(asyncResp->res); 472 return; 473 } 474 475 // used for making maps out of 2 vectors 476 const std::vector<double>* keys = nullptr; 477 const std::vector<double>* values = nullptr; 478 479 for (const auto& propertyPair : intfPair.second) 480 { 481 if (propertyPair.first == "Type" || 482 propertyPair.first == "Class" || 483 propertyPair.first == "Name") 484 { 485 continue; 486 } 487 488 // zones 489 if (intfPair.first == pidZoneConfigurationIface) 490 { 491 const double* ptr = 492 std::get_if<double>(&propertyPair.second); 493 if (ptr == nullptr) 494 { 495 BMCWEB_LOG_ERROR("Field Illegal {}", 496 propertyPair.first); 497 messages::internalError(asyncResp->res); 498 return; 499 } 500 (*config)[propertyPair.first] = *ptr; 501 } 502 503 if (intfPair.first == stepwiseConfigurationIface) 504 { 505 if (propertyPair.first == "Reading" || 506 propertyPair.first == "Output") 507 { 508 const std::vector<double>* ptr = 509 std::get_if<std::vector<double>>( 510 &propertyPair.second); 511 512 if (ptr == nullptr) 513 { 514 BMCWEB_LOG_ERROR("Field Illegal {}", 515 propertyPair.first); 516 messages::internalError(asyncResp->res); 517 return; 518 } 519 520 if (propertyPair.first == "Reading") 521 { 522 keys = ptr; 523 } 524 else 525 { 526 values = ptr; 527 } 528 if (keys != nullptr && values != nullptr) 529 { 530 if (keys->size() != values->size()) 531 { 532 BMCWEB_LOG_ERROR( 533 "Reading and Output size don't match "); 534 messages::internalError(asyncResp->res); 535 return; 536 } 537 nlohmann::json& steps = (*config)["Steps"]; 538 steps = nlohmann::json::array(); 539 for (size_t ii = 0; ii < keys->size(); ii++) 540 { 541 nlohmann::json::object_t step; 542 step["Target"] = (*keys)[ii]; 543 step["Output"] = (*values)[ii]; 544 steps.emplace_back(std::move(step)); 545 } 546 } 547 } 548 if (propertyPair.first == "NegativeHysteresis" || 549 propertyPair.first == "PositiveHysteresis") 550 { 551 const double* ptr = 552 std::get_if<double>(&propertyPair.second); 553 if (ptr == nullptr) 554 { 555 BMCWEB_LOG_ERROR("Field Illegal {}", 556 propertyPair.first); 557 messages::internalError(asyncResp->res); 558 return; 559 } 560 (*config)[propertyPair.first] = *ptr; 561 } 562 } 563 564 // pid and fans are off the same configuration 565 if (intfPair.first == pidConfigurationIface || 566 intfPair.first == stepwiseConfigurationIface) 567 { 568 if (propertyPair.first == "Zones") 569 { 570 const std::vector<std::string>* inputs = 571 std::get_if<std::vector<std::string>>( 572 &propertyPair.second); 573 574 if (inputs == nullptr) 575 { 576 BMCWEB_LOG_ERROR("Zones Pid Field Illegal"); 577 messages::internalError(asyncResp->res); 578 return; 579 } 580 auto& data = (*config)[propertyPair.first]; 581 data = nlohmann::json::array(); 582 for (std::string itemCopy : *inputs) 583 { 584 dbus::utility::escapePathForDbus(itemCopy); 585 nlohmann::json::object_t input; 586 boost::urls::url managerUrl = boost::urls::format( 587 "/redfish/v1/Managers/bmc#{}", 588 ("/Oem/OpenBmc/Fan/FanZones"_json_pointer / 589 itemCopy) 590 .to_string()); 591 input["@odata.id"] = std::move(managerUrl); 592 data.emplace_back(std::move(input)); 593 } 594 } 595 // todo(james): may never happen, but this 596 // assumes configuration data referenced in the 597 // PID config is provided by the same daemon, we 598 // could add another loop to cover all cases, 599 // but I'm okay kicking this can down the road a 600 // bit 601 602 else if (propertyPair.first == "Inputs" || 603 propertyPair.first == "Outputs") 604 { 605 auto& data = (*config)[propertyPair.first]; 606 const std::vector<std::string>* inputs = 607 std::get_if<std::vector<std::string>>( 608 &propertyPair.second); 609 610 if (inputs == nullptr) 611 { 612 BMCWEB_LOG_ERROR("Field Illegal {}", 613 propertyPair.first); 614 messages::internalError(asyncResp->res); 615 return; 616 } 617 data = *inputs; 618 } 619 else if (propertyPair.first == "SetPointOffset") 620 { 621 const std::string* ptr = 622 std::get_if<std::string>(&propertyPair.second); 623 624 if (ptr == nullptr) 625 { 626 BMCWEB_LOG_ERROR("Field Illegal {}", 627 propertyPair.first); 628 messages::internalError(asyncResp->res); 629 return; 630 } 631 // translate from dbus to redfish 632 if (*ptr == "WarningHigh") 633 { 634 (*config)["SetPointOffset"] = 635 "UpperThresholdNonCritical"; 636 } 637 else if (*ptr == "WarningLow") 638 { 639 (*config)["SetPointOffset"] = 640 "LowerThresholdNonCritical"; 641 } 642 else if (*ptr == "CriticalHigh") 643 { 644 (*config)["SetPointOffset"] = 645 "UpperThresholdCritical"; 646 } 647 else if (*ptr == "CriticalLow") 648 { 649 (*config)["SetPointOffset"] = 650 "LowerThresholdCritical"; 651 } 652 else 653 { 654 BMCWEB_LOG_ERROR("Value Illegal {}", *ptr); 655 messages::internalError(asyncResp->res); 656 return; 657 } 658 } 659 // doubles 660 else if (propertyPair.first == "FFGainCoefficient" || 661 propertyPair.first == "FFOffCoefficient" || 662 propertyPair.first == "ICoefficient" || 663 propertyPair.first == "ILimitMax" || 664 propertyPair.first == "ILimitMin" || 665 propertyPair.first == "PositiveHysteresis" || 666 propertyPair.first == "NegativeHysteresis" || 667 propertyPair.first == "OutLimitMax" || 668 propertyPair.first == "OutLimitMin" || 669 propertyPair.first == "PCoefficient" || 670 propertyPair.first == "SetPoint" || 671 propertyPair.first == "SlewNeg" || 672 propertyPair.first == "SlewPos") 673 { 674 const double* ptr = 675 std::get_if<double>(&propertyPair.second); 676 if (ptr == nullptr) 677 { 678 BMCWEB_LOG_ERROR("Field Illegal {}", 679 propertyPair.first); 680 messages::internalError(asyncResp->res); 681 return; 682 } 683 (*config)[propertyPair.first] = *ptr; 684 } 685 } 686 } 687 } 688 } 689 }); 690 } 691 692 enum class CreatePIDRet 693 { 694 fail, 695 del, 696 patch 697 }; 698 699 inline bool 700 getZonesFromJsonReq(const std::shared_ptr<bmcweb::AsyncResp>& response, 701 std::vector<nlohmann::json>& config, 702 std::vector<std::string>& zones) 703 { 704 if (config.empty()) 705 { 706 BMCWEB_LOG_ERROR("Empty Zones"); 707 messages::propertyValueFormatError(response->res, config, "Zones"); 708 return false; 709 } 710 for (auto& odata : config) 711 { 712 std::string path; 713 if (!redfish::json_util::readJson(odata, response->res, "@odata.id", 714 path)) 715 { 716 return false; 717 } 718 std::string input; 719 720 // 8 below comes from 721 // /redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/Left 722 // 0 1 2 3 4 5 6 7 8 723 if (!dbus::utility::getNthStringFromPath(path, 8, input)) 724 { 725 BMCWEB_LOG_ERROR("Got invalid path {}", path); 726 BMCWEB_LOG_ERROR("Illegal Type Zones"); 727 messages::propertyValueFormatError(response->res, odata, "Zones"); 728 return false; 729 } 730 std::replace(input.begin(), input.end(), '_', ' '); 731 zones.emplace_back(std::move(input)); 732 } 733 return true; 734 } 735 736 inline const dbus::utility::ManagedObjectType::value_type* 737 findChassis(const dbus::utility::ManagedObjectType& managedObj, 738 const std::string& value, std::string& chassis) 739 { 740 BMCWEB_LOG_DEBUG("Find Chassis: {}", value); 741 742 std::string escaped = value; 743 std::replace(escaped.begin(), escaped.end(), ' ', '_'); 744 escaped = "/" + escaped; 745 auto it = std::ranges::find_if(managedObj, [&escaped](const auto& obj) { 746 if (boost::algorithm::ends_with(obj.first.str, escaped)) 747 { 748 BMCWEB_LOG_DEBUG("Matched {}", obj.first.str); 749 return true; 750 } 751 return false; 752 }); 753 754 if (it == managedObj.end()) 755 { 756 return nullptr; 757 } 758 // 5 comes from <chassis-name> being the 5th element 759 // /xyz/openbmc_project/inventory/system/chassis/<chassis-name> 760 if (dbus::utility::getNthStringFromPath(it->first.str, 5, chassis)) 761 { 762 return &(*it); 763 } 764 765 return nullptr; 766 } 767 768 inline CreatePIDRet createPidInterface( 769 const std::shared_ptr<bmcweb::AsyncResp>& response, const std::string& type, 770 const nlohmann::json::iterator& it, const std::string& path, 771 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject, 772 dbus::utility::DBusPropertiesMap& output, std::string& chassis, 773 const std::string& profile) 774 { 775 // common deleter 776 if (it.value() == nullptr) 777 { 778 std::string iface; 779 if (type == "PidControllers" || type == "FanControllers") 780 { 781 iface = pidConfigurationIface; 782 } 783 else if (type == "FanZones") 784 { 785 iface = pidZoneConfigurationIface; 786 } 787 else if (type == "StepwiseControllers") 788 { 789 iface = stepwiseConfigurationIface; 790 } 791 else 792 { 793 BMCWEB_LOG_ERROR("Illegal Type {}", type); 794 messages::propertyUnknown(response->res, type); 795 return CreatePIDRet::fail; 796 } 797 798 BMCWEB_LOG_DEBUG("del {} {}", path, iface); 799 // delete interface 800 crow::connections::systemBus->async_method_call( 801 [response, path](const boost::system::error_code& ec) { 802 if (ec) 803 { 804 BMCWEB_LOG_ERROR("Error patching {}: {}", path, ec); 805 messages::internalError(response->res); 806 return; 807 } 808 messages::success(response->res); 809 }, 810 "xyz.openbmc_project.EntityManager", path, iface, "Delete"); 811 return CreatePIDRet::del; 812 } 813 814 const dbus::utility::ManagedObjectType::value_type* managedItem = nullptr; 815 if (!createNewObject) 816 { 817 // if we aren't creating a new object, we should be able to find it on 818 // d-bus 819 managedItem = findChassis(managedObj, it.key(), chassis); 820 if (managedItem == nullptr) 821 { 822 BMCWEB_LOG_ERROR("Failed to get chassis from config patch"); 823 messages::invalidObject( 824 response->res, 825 boost::urls::format("/redfish/v1/Chassis/{}", chassis)); 826 return CreatePIDRet::fail; 827 } 828 } 829 830 if (!profile.empty() && 831 (type == "PidControllers" || type == "FanControllers" || 832 type == "StepwiseControllers")) 833 { 834 if (managedItem == nullptr) 835 { 836 output.emplace_back("Profiles", std::vector<std::string>{profile}); 837 } 838 else 839 { 840 std::string interface; 841 if (type == "StepwiseControllers") 842 { 843 interface = stepwiseConfigurationIface; 844 } 845 else 846 { 847 interface = pidConfigurationIface; 848 } 849 bool ifaceFound = false; 850 for (const auto& iface : managedItem->second) 851 { 852 if (iface.first == interface) 853 { 854 ifaceFound = true; 855 for (const auto& prop : iface.second) 856 { 857 if (prop.first == "Profiles") 858 { 859 const std::vector<std::string>* curProfiles = 860 std::get_if<std::vector<std::string>>( 861 &(prop.second)); 862 if (curProfiles == nullptr) 863 { 864 BMCWEB_LOG_ERROR( 865 "Illegal profiles in managed object"); 866 messages::internalError(response->res); 867 return CreatePIDRet::fail; 868 } 869 if (std::find(curProfiles->begin(), 870 curProfiles->end(), 871 profile) == curProfiles->end()) 872 { 873 std::vector<std::string> newProfiles = 874 *curProfiles; 875 newProfiles.push_back(profile); 876 output.emplace_back("Profiles", newProfiles); 877 } 878 } 879 } 880 } 881 } 882 883 if (!ifaceFound) 884 { 885 BMCWEB_LOG_ERROR("Failed to find interface in managed object"); 886 messages::internalError(response->res); 887 return CreatePIDRet::fail; 888 } 889 } 890 } 891 892 if (type == "PidControllers" || type == "FanControllers") 893 { 894 if (createNewObject) 895 { 896 output.emplace_back("Class", 897 type == "PidControllers" ? "temp" : "fan"); 898 output.emplace_back("Type", "Pid"); 899 } 900 901 std::optional<std::vector<nlohmann::json>> zones; 902 std::optional<std::vector<std::string>> inputs; 903 std::optional<std::vector<std::string>> outputs; 904 std::map<std::string, std::optional<double>> doubles; 905 std::optional<std::string> setpointOffset; 906 if (!redfish::json_util::readJson( 907 it.value(), response->res, "Inputs", inputs, "Outputs", outputs, 908 "Zones", zones, "FFGainCoefficient", 909 doubles["FFGainCoefficient"], "FFOffCoefficient", 910 doubles["FFOffCoefficient"], "ICoefficient", 911 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"], 912 "ILimitMin", doubles["ILimitMin"], "OutLimitMax", 913 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"], 914 "PCoefficient", doubles["PCoefficient"], "SetPoint", 915 doubles["SetPoint"], "SetPointOffset", setpointOffset, 916 "SlewNeg", doubles["SlewNeg"], "SlewPos", doubles["SlewPos"], 917 "PositiveHysteresis", doubles["PositiveHysteresis"], 918 "NegativeHysteresis", doubles["NegativeHysteresis"])) 919 { 920 return CreatePIDRet::fail; 921 } 922 if (zones) 923 { 924 std::vector<std::string> zonesStr; 925 if (!getZonesFromJsonReq(response, *zones, zonesStr)) 926 { 927 BMCWEB_LOG_ERROR("Illegal Zones"); 928 return CreatePIDRet::fail; 929 } 930 if (chassis.empty() && 931 findChassis(managedObj, zonesStr[0], chassis) == nullptr) 932 { 933 BMCWEB_LOG_ERROR("Failed to get chassis from config patch"); 934 messages::invalidObject( 935 response->res, 936 boost::urls::format("/redfish/v1/Chassis/{}", chassis)); 937 return CreatePIDRet::fail; 938 } 939 output.emplace_back("Zones", std::move(zonesStr)); 940 } 941 942 if (inputs) 943 { 944 for (std::string& value : *inputs) 945 { 946 std::replace(value.begin(), value.end(), '_', ' '); 947 } 948 output.emplace_back("Inputs", *inputs); 949 } 950 951 if (outputs) 952 { 953 for (std::string& value : *outputs) 954 { 955 std::replace(value.begin(), value.end(), '_', ' '); 956 } 957 output.emplace_back("Outputs", *outputs); 958 } 959 960 if (setpointOffset) 961 { 962 // translate between redfish and dbus names 963 if (*setpointOffset == "UpperThresholdNonCritical") 964 { 965 output.emplace_back("SetPointOffset", "WarningLow"); 966 } 967 else if (*setpointOffset == "LowerThresholdNonCritical") 968 { 969 output.emplace_back("SetPointOffset", "WarningHigh"); 970 } 971 else if (*setpointOffset == "LowerThresholdCritical") 972 { 973 output.emplace_back("SetPointOffset", "CriticalLow"); 974 } 975 else if (*setpointOffset == "UpperThresholdCritical") 976 { 977 output.emplace_back("SetPointOffset", "CriticalHigh"); 978 } 979 else 980 { 981 BMCWEB_LOG_ERROR("Invalid setpointoffset {}", *setpointOffset); 982 messages::propertyValueNotInList(response->res, it.key(), 983 "SetPointOffset"); 984 return CreatePIDRet::fail; 985 } 986 } 987 988 // doubles 989 for (const auto& pairs : doubles) 990 { 991 if (!pairs.second) 992 { 993 continue; 994 } 995 BMCWEB_LOG_DEBUG("{} = {}", pairs.first, *pairs.second); 996 output.emplace_back(pairs.first, *pairs.second); 997 } 998 } 999 1000 else if (type == "FanZones") 1001 { 1002 output.emplace_back("Type", "Pid.Zone"); 1003 1004 std::optional<nlohmann::json> chassisContainer; 1005 std::optional<double> failSafePercent; 1006 std::optional<double> minThermalOutput; 1007 if (!redfish::json_util::readJson(it.value(), response->res, "Chassis", 1008 chassisContainer, "FailSafePercent", 1009 failSafePercent, "MinThermalOutput", 1010 minThermalOutput)) 1011 { 1012 return CreatePIDRet::fail; 1013 } 1014 1015 if (chassisContainer) 1016 { 1017 std::string chassisId; 1018 if (!redfish::json_util::readJson(*chassisContainer, response->res, 1019 "@odata.id", chassisId)) 1020 { 1021 return CreatePIDRet::fail; 1022 } 1023 1024 // /redfish/v1/chassis/chassis_name/ 1025 if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis)) 1026 { 1027 BMCWEB_LOG_ERROR("Got invalid path {}", chassisId); 1028 messages::invalidObject( 1029 response->res, 1030 boost::urls::format("/redfish/v1/Chassis/{}", chassisId)); 1031 return CreatePIDRet::fail; 1032 } 1033 } 1034 if (minThermalOutput) 1035 { 1036 output.emplace_back("MinThermalOutput", *minThermalOutput); 1037 } 1038 if (failSafePercent) 1039 { 1040 output.emplace_back("FailSafePercent", *failSafePercent); 1041 } 1042 } 1043 else if (type == "StepwiseControllers") 1044 { 1045 output.emplace_back("Type", "Stepwise"); 1046 1047 std::optional<std::vector<nlohmann::json>> zones; 1048 std::optional<std::vector<nlohmann::json>> steps; 1049 std::optional<std::vector<std::string>> inputs; 1050 std::optional<double> positiveHysteresis; 1051 std::optional<double> negativeHysteresis; 1052 std::optional<std::string> direction; // upper clipping curve vs lower 1053 if (!redfish::json_util::readJson( 1054 it.value(), response->res, "Zones", zones, "Steps", steps, 1055 "Inputs", inputs, "PositiveHysteresis", positiveHysteresis, 1056 "NegativeHysteresis", negativeHysteresis, "Direction", 1057 direction)) 1058 { 1059 return CreatePIDRet::fail; 1060 } 1061 1062 if (zones) 1063 { 1064 std::vector<std::string> zonesStrs; 1065 if (!getZonesFromJsonReq(response, *zones, zonesStrs)) 1066 { 1067 BMCWEB_LOG_ERROR("Illegal Zones"); 1068 return CreatePIDRet::fail; 1069 } 1070 if (chassis.empty() && 1071 findChassis(managedObj, zonesStrs[0], chassis) == nullptr) 1072 { 1073 BMCWEB_LOG_ERROR("Failed to get chassis from config patch"); 1074 messages::invalidObject( 1075 response->res, 1076 boost::urls::format("/redfish/v1/Chassis/{}", chassis)); 1077 return CreatePIDRet::fail; 1078 } 1079 output.emplace_back("Zones", std::move(zonesStrs)); 1080 } 1081 if (steps) 1082 { 1083 std::vector<double> readings; 1084 std::vector<double> outputs; 1085 for (auto& step : *steps) 1086 { 1087 double target = 0.0; 1088 double out = 0.0; 1089 1090 if (!redfish::json_util::readJson(step, response->res, "Target", 1091 target, "Output", out)) 1092 { 1093 return CreatePIDRet::fail; 1094 } 1095 readings.emplace_back(target); 1096 outputs.emplace_back(out); 1097 } 1098 output.emplace_back("Reading", std::move(readings)); 1099 output.emplace_back("Output", std::move(outputs)); 1100 } 1101 if (inputs) 1102 { 1103 for (std::string& value : *inputs) 1104 { 1105 std::replace(value.begin(), value.end(), '_', ' '); 1106 } 1107 output.emplace_back("Inputs", std::move(*inputs)); 1108 } 1109 if (negativeHysteresis) 1110 { 1111 output.emplace_back("NegativeHysteresis", *negativeHysteresis); 1112 } 1113 if (positiveHysteresis) 1114 { 1115 output.emplace_back("PositiveHysteresis", *positiveHysteresis); 1116 } 1117 if (direction) 1118 { 1119 constexpr const std::array<const char*, 2> allowedDirections = { 1120 "Ceiling", "Floor"}; 1121 if (std::ranges::find(allowedDirections, *direction) == 1122 allowedDirections.end()) 1123 { 1124 messages::propertyValueTypeError(response->res, "Direction", 1125 *direction); 1126 return CreatePIDRet::fail; 1127 } 1128 output.emplace_back("Class", *direction); 1129 } 1130 } 1131 else 1132 { 1133 BMCWEB_LOG_ERROR("Illegal Type {}", type); 1134 messages::propertyUnknown(response->res, type); 1135 return CreatePIDRet::fail; 1136 } 1137 return CreatePIDRet::patch; 1138 } 1139 struct GetPIDValues : std::enable_shared_from_this<GetPIDValues> 1140 { 1141 struct CompletionValues 1142 { 1143 std::vector<std::string> supportedProfiles; 1144 std::string currentProfile; 1145 dbus::utility::MapperGetSubTreeResponse subtree; 1146 }; 1147 1148 explicit GetPIDValues( 1149 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) : 1150 asyncResp(asyncRespIn) 1151 1152 {} 1153 1154 void run() 1155 { 1156 std::shared_ptr<GetPIDValues> self = shared_from_this(); 1157 1158 // get all configurations 1159 constexpr std::array<std::string_view, 4> interfaces = { 1160 pidConfigurationIface, pidZoneConfigurationIface, 1161 objectManagerIface, stepwiseConfigurationIface}; 1162 dbus::utility::getSubTree( 1163 "/", 0, interfaces, 1164 [self]( 1165 const boost::system::error_code& ec, 1166 const dbus::utility::MapperGetSubTreeResponse& subtreeLocal) { 1167 if (ec) 1168 { 1169 BMCWEB_LOG_ERROR("{}", ec); 1170 messages::internalError(self->asyncResp->res); 1171 return; 1172 } 1173 self->complete.subtree = subtreeLocal; 1174 }); 1175 1176 // at the same time get the selected profile 1177 constexpr std::array<std::string_view, 1> thermalModeIfaces = { 1178 thermalModeIface}; 1179 dbus::utility::getSubTree( 1180 "/", 0, thermalModeIfaces, 1181 [self]( 1182 const boost::system::error_code& ec, 1183 const dbus::utility::MapperGetSubTreeResponse& subtreeLocal) { 1184 if (ec || subtreeLocal.empty()) 1185 { 1186 return; 1187 } 1188 if (subtreeLocal[0].second.size() != 1) 1189 { 1190 // invalid mapper response, should never happen 1191 BMCWEB_LOG_ERROR("GetPIDValues: Mapper Error"); 1192 messages::internalError(self->asyncResp->res); 1193 return; 1194 } 1195 1196 const std::string& path = subtreeLocal[0].first; 1197 const std::string& owner = subtreeLocal[0].second[0].first; 1198 1199 sdbusplus::asio::getAllProperties( 1200 *crow::connections::systemBus, owner, path, thermalModeIface, 1201 [path, owner, 1202 self](const boost::system::error_code& ec2, 1203 const dbus::utility::DBusPropertiesMap& resp) { 1204 if (ec2) 1205 { 1206 BMCWEB_LOG_ERROR( 1207 "GetPIDValues: Can't get thermalModeIface {}", path); 1208 messages::internalError(self->asyncResp->res); 1209 return; 1210 } 1211 1212 const std::string* current = nullptr; 1213 const std::vector<std::string>* supported = nullptr; 1214 1215 const bool success = sdbusplus::unpackPropertiesNoThrow( 1216 dbus_utils::UnpackErrorPrinter(), resp, "Current", current, 1217 "Supported", supported); 1218 1219 if (!success) 1220 { 1221 messages::internalError(self->asyncResp->res); 1222 return; 1223 } 1224 1225 if (current == nullptr || supported == nullptr) 1226 { 1227 BMCWEB_LOG_ERROR( 1228 "GetPIDValues: thermal mode iface invalid {}", path); 1229 messages::internalError(self->asyncResp->res); 1230 return; 1231 } 1232 self->complete.currentProfile = *current; 1233 self->complete.supportedProfiles = *supported; 1234 }); 1235 }); 1236 } 1237 1238 static void 1239 processingComplete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1240 const CompletionValues& completion) 1241 { 1242 if (asyncResp->res.result() != boost::beast::http::status::ok) 1243 { 1244 return; 1245 } 1246 // create map of <connection, path to objMgr>> 1247 boost::container::flat_map< 1248 std::string, std::string, std::less<>, 1249 std::vector<std::pair<std::string, std::string>>> 1250 objectMgrPaths; 1251 boost::container::flat_set<std::string, std::less<>, 1252 std::vector<std::string>> 1253 calledConnections; 1254 for (const auto& pathGroup : completion.subtree) 1255 { 1256 for (const auto& connectionGroup : pathGroup.second) 1257 { 1258 auto findConnection = 1259 calledConnections.find(connectionGroup.first); 1260 if (findConnection != calledConnections.end()) 1261 { 1262 break; 1263 } 1264 for (const std::string& interface : connectionGroup.second) 1265 { 1266 if (interface == objectManagerIface) 1267 { 1268 objectMgrPaths[connectionGroup.first] = pathGroup.first; 1269 } 1270 // this list is alphabetical, so we 1271 // should have found the objMgr by now 1272 if (interface == pidConfigurationIface || 1273 interface == pidZoneConfigurationIface || 1274 interface == stepwiseConfigurationIface) 1275 { 1276 auto findObjMgr = 1277 objectMgrPaths.find(connectionGroup.first); 1278 if (findObjMgr == objectMgrPaths.end()) 1279 { 1280 BMCWEB_LOG_DEBUG("{}Has no Object Manager", 1281 connectionGroup.first); 1282 continue; 1283 } 1284 1285 calledConnections.insert(connectionGroup.first); 1286 1287 asyncPopulatePid(findObjMgr->first, findObjMgr->second, 1288 completion.currentProfile, 1289 completion.supportedProfiles, 1290 asyncResp); 1291 break; 1292 } 1293 } 1294 } 1295 } 1296 } 1297 1298 ~GetPIDValues() 1299 { 1300 boost::asio::post(crow::connections::systemBus->get_io_context(), 1301 std::bind_front(&processingComplete, asyncResp, 1302 std::move(complete))); 1303 } 1304 1305 GetPIDValues(const GetPIDValues&) = delete; 1306 GetPIDValues(GetPIDValues&&) = delete; 1307 GetPIDValues& operator=(const GetPIDValues&) = delete; 1308 GetPIDValues& operator=(GetPIDValues&&) = delete; 1309 1310 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1311 CompletionValues complete; 1312 }; 1313 1314 struct SetPIDValues : std::enable_shared_from_this<SetPIDValues> 1315 { 1316 SetPIDValues(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn, 1317 nlohmann::json& data) : 1318 asyncResp(asyncRespIn) 1319 { 1320 std::optional<nlohmann::json> pidControllers; 1321 std::optional<nlohmann::json> fanControllers; 1322 std::optional<nlohmann::json> fanZones; 1323 std::optional<nlohmann::json> stepwiseControllers; 1324 1325 if (!redfish::json_util::readJson( 1326 data, asyncResp->res, "PidControllers", pidControllers, 1327 "FanControllers", fanControllers, "FanZones", fanZones, 1328 "StepwiseControllers", stepwiseControllers, "Profile", profile)) 1329 { 1330 return; 1331 } 1332 configuration.emplace_back("PidControllers", std::move(pidControllers)); 1333 configuration.emplace_back("FanControllers", std::move(fanControllers)); 1334 configuration.emplace_back("FanZones", std::move(fanZones)); 1335 configuration.emplace_back("StepwiseControllers", 1336 std::move(stepwiseControllers)); 1337 } 1338 1339 SetPIDValues(const SetPIDValues&) = delete; 1340 SetPIDValues(SetPIDValues&&) = delete; 1341 SetPIDValues& operator=(const SetPIDValues&) = delete; 1342 SetPIDValues& operator=(SetPIDValues&&) = delete; 1343 1344 void run() 1345 { 1346 if (asyncResp->res.result() != boost::beast::http::status::ok) 1347 { 1348 return; 1349 } 1350 1351 std::shared_ptr<SetPIDValues> self = shared_from_this(); 1352 1353 // todo(james): might make sense to do a mapper call here if this 1354 // interface gets more traction 1355 sdbusplus::message::object_path objPath( 1356 "/xyz/openbmc_project/inventory"); 1357 dbus::utility::getManagedObjects( 1358 "xyz.openbmc_project.EntityManager", objPath, 1359 [self](const boost::system::error_code& ec, 1360 const dbus::utility::ManagedObjectType& mObj) { 1361 if (ec) 1362 { 1363 BMCWEB_LOG_ERROR("Error communicating to Entity Manager"); 1364 messages::internalError(self->asyncResp->res); 1365 return; 1366 } 1367 const std::array<const char*, 3> configurations = { 1368 pidConfigurationIface, pidZoneConfigurationIface, 1369 stepwiseConfigurationIface}; 1370 1371 for (const auto& [path, object] : mObj) 1372 { 1373 for (const auto& [interface, _] : object) 1374 { 1375 if (std::ranges::find(configurations, interface) != 1376 configurations.end()) 1377 { 1378 self->objectCount++; 1379 break; 1380 } 1381 } 1382 } 1383 self->managedObj = mObj; 1384 }); 1385 1386 // at the same time get the profile information 1387 constexpr std::array<std::string_view, 1> thermalModeIfaces = { 1388 thermalModeIface}; 1389 dbus::utility::getSubTree( 1390 "/", 0, thermalModeIfaces, 1391 [self](const boost::system::error_code& ec, 1392 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1393 if (ec || subtree.empty()) 1394 { 1395 return; 1396 } 1397 if (subtree[0].second.empty()) 1398 { 1399 // invalid mapper response, should never happen 1400 BMCWEB_LOG_ERROR("SetPIDValues: Mapper Error"); 1401 messages::internalError(self->asyncResp->res); 1402 return; 1403 } 1404 1405 const std::string& path = subtree[0].first; 1406 const std::string& owner = subtree[0].second[0].first; 1407 sdbusplus::asio::getAllProperties( 1408 *crow::connections::systemBus, owner, path, thermalModeIface, 1409 [self, path, owner](const boost::system::error_code& ec2, 1410 const dbus::utility::DBusPropertiesMap& r) { 1411 if (ec2) 1412 { 1413 BMCWEB_LOG_ERROR( 1414 "SetPIDValues: Can't get thermalModeIface {}", path); 1415 messages::internalError(self->asyncResp->res); 1416 return; 1417 } 1418 const std::string* current = nullptr; 1419 const std::vector<std::string>* supported = nullptr; 1420 1421 const bool success = sdbusplus::unpackPropertiesNoThrow( 1422 dbus_utils::UnpackErrorPrinter(), r, "Current", current, 1423 "Supported", supported); 1424 1425 if (!success) 1426 { 1427 messages::internalError(self->asyncResp->res); 1428 return; 1429 } 1430 1431 if (current == nullptr || supported == nullptr) 1432 { 1433 BMCWEB_LOG_ERROR( 1434 "SetPIDValues: thermal mode iface invalid {}", path); 1435 messages::internalError(self->asyncResp->res); 1436 return; 1437 } 1438 self->currentProfile = *current; 1439 self->supportedProfiles = *supported; 1440 self->profileConnection = owner; 1441 self->profilePath = path; 1442 }); 1443 }); 1444 } 1445 void pidSetDone() 1446 { 1447 if (asyncResp->res.result() != boost::beast::http::status::ok) 1448 { 1449 return; 1450 } 1451 std::shared_ptr<bmcweb::AsyncResp> response = asyncResp; 1452 if (profile) 1453 { 1454 if (std::ranges::find(supportedProfiles, *profile) == 1455 supportedProfiles.end()) 1456 { 1457 messages::actionParameterUnknown(response->res, "Profile", 1458 *profile); 1459 return; 1460 } 1461 currentProfile = *profile; 1462 sdbusplus::asio::setProperty( 1463 *crow::connections::systemBus, profileConnection, profilePath, 1464 thermalModeIface, "Current", *profile, 1465 [response](const boost::system::error_code& ec) { 1466 if (ec) 1467 { 1468 BMCWEB_LOG_ERROR("Error patching profile{}", ec); 1469 messages::internalError(response->res); 1470 } 1471 }); 1472 } 1473 1474 for (auto& containerPair : configuration) 1475 { 1476 auto& container = containerPair.second; 1477 if (!container) 1478 { 1479 continue; 1480 } 1481 BMCWEB_LOG_DEBUG("{}", *container); 1482 1483 const std::string& type = containerPair.first; 1484 1485 for (nlohmann::json::iterator it = container->begin(); 1486 it != container->end(); ++it) 1487 { 1488 const auto& name = it.key(); 1489 std::string dbusObjName = name; 1490 std::replace(dbusObjName.begin(), dbusObjName.end(), ' ', '_'); 1491 BMCWEB_LOG_DEBUG("looking for {}", name); 1492 1493 auto pathItr = std::ranges::find_if( 1494 managedObj, [&dbusObjName](const auto& obj) { 1495 return boost::algorithm::ends_with(obj.first.str, 1496 "/" + dbusObjName); 1497 }); 1498 dbus::utility::DBusPropertiesMap output; 1499 1500 output.reserve(16); // The pid interface length 1501 1502 // determines if we're patching entity-manager or 1503 // creating a new object 1504 bool createNewObject = (pathItr == managedObj.end()); 1505 BMCWEB_LOG_DEBUG("Found = {}", !createNewObject); 1506 1507 std::string iface; 1508 if (!createNewObject) 1509 { 1510 bool findInterface = false; 1511 for (const auto& interface : pathItr->second) 1512 { 1513 if (interface.first == pidConfigurationIface) 1514 { 1515 if (type == "PidControllers" || 1516 type == "FanControllers") 1517 { 1518 iface = pidConfigurationIface; 1519 findInterface = true; 1520 break; 1521 } 1522 } 1523 else if (interface.first == pidZoneConfigurationIface) 1524 { 1525 if (type == "FanZones") 1526 { 1527 iface = pidConfigurationIface; 1528 findInterface = true; 1529 break; 1530 } 1531 } 1532 else if (interface.first == stepwiseConfigurationIface) 1533 { 1534 if (type == "StepwiseControllers") 1535 { 1536 iface = stepwiseConfigurationIface; 1537 findInterface = true; 1538 break; 1539 } 1540 } 1541 } 1542 1543 // create new object if interface not found 1544 if (!findInterface) 1545 { 1546 createNewObject = true; 1547 } 1548 } 1549 1550 if (createNewObject && it.value() == nullptr) 1551 { 1552 // can't delete a non-existent object 1553 messages::propertyValueNotInList(response->res, it.value(), 1554 name); 1555 continue; 1556 } 1557 1558 std::string path; 1559 if (pathItr != managedObj.end()) 1560 { 1561 path = pathItr->first.str; 1562 } 1563 1564 BMCWEB_LOG_DEBUG("Create new = {}", createNewObject); 1565 1566 // arbitrary limit to avoid attacks 1567 constexpr const size_t controllerLimit = 500; 1568 if (createNewObject && objectCount >= controllerLimit) 1569 { 1570 messages::resourceExhaustion(response->res, type); 1571 continue; 1572 } 1573 std::string escaped = name; 1574 std::replace(escaped.begin(), escaped.end(), '_', ' '); 1575 output.emplace_back("Name", escaped); 1576 1577 std::string chassis; 1578 CreatePIDRet ret = createPidInterface( 1579 response, type, it, path, managedObj, createNewObject, 1580 output, chassis, currentProfile); 1581 if (ret == CreatePIDRet::fail) 1582 { 1583 return; 1584 } 1585 if (ret == CreatePIDRet::del) 1586 { 1587 continue; 1588 } 1589 1590 if (!createNewObject) 1591 { 1592 for (const auto& property : output) 1593 { 1594 sdbusplus::asio::setProperty( 1595 *crow::connections::systemBus, 1596 "xyz.openbmc_project.EntityManager", path, iface, 1597 property.first, property.second, 1598 [response, 1599 propertyName{std::string(property.first)}]( 1600 const boost::system::error_code& ec) { 1601 if (ec) 1602 { 1603 BMCWEB_LOG_ERROR("Error patching {}: {}", 1604 propertyName, ec); 1605 messages::internalError(response->res); 1606 return; 1607 } 1608 messages::success(response->res); 1609 }); 1610 } 1611 } 1612 else 1613 { 1614 if (chassis.empty()) 1615 { 1616 BMCWEB_LOG_ERROR("Failed to get chassis from config"); 1617 messages::internalError(response->res); 1618 return; 1619 } 1620 1621 bool foundChassis = false; 1622 for (const auto& obj : managedObj) 1623 { 1624 if (boost::algorithm::ends_with(obj.first.str, chassis)) 1625 { 1626 chassis = obj.first.str; 1627 foundChassis = true; 1628 break; 1629 } 1630 } 1631 if (!foundChassis) 1632 { 1633 BMCWEB_LOG_ERROR("Failed to find chassis on dbus"); 1634 messages::resourceMissingAtURI( 1635 response->res, 1636 boost::urls::format("/redfish/v1/Chassis/{}", 1637 chassis)); 1638 return; 1639 } 1640 1641 crow::connections::systemBus->async_method_call( 1642 [response](const boost::system::error_code& ec) { 1643 if (ec) 1644 { 1645 BMCWEB_LOG_ERROR("Error Adding Pid Object {}", ec); 1646 messages::internalError(response->res); 1647 return; 1648 } 1649 messages::success(response->res); 1650 }, 1651 "xyz.openbmc_project.EntityManager", chassis, 1652 "xyz.openbmc_project.AddObject", "AddObject", output); 1653 } 1654 } 1655 } 1656 } 1657 1658 ~SetPIDValues() 1659 { 1660 try 1661 { 1662 pidSetDone(); 1663 } 1664 catch (...) 1665 { 1666 BMCWEB_LOG_CRITICAL("pidSetDone threw exception"); 1667 } 1668 } 1669 1670 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1671 std::vector<std::pair<std::string, std::optional<nlohmann::json>>> 1672 configuration; 1673 std::optional<std::string> profile; 1674 dbus::utility::ManagedObjectType managedObj; 1675 std::vector<std::string> supportedProfiles; 1676 std::string currentProfile; 1677 std::string profileConnection; 1678 std::string profilePath; 1679 size_t objectCount = 0; 1680 }; 1681 1682 /** 1683 * @brief Retrieves BMC manager location data over DBus 1684 * 1685 * @param[in] asyncResp Shared pointer for completing asynchronous calls 1686 * @param[in] connectionName - service name 1687 * @param[in] path - object path 1688 * @return none 1689 */ 1690 inline void getLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1691 const std::string& connectionName, 1692 const std::string& path) 1693 { 1694 BMCWEB_LOG_DEBUG("Get BMC manager Location data."); 1695 1696 sdbusplus::asio::getProperty<std::string>( 1697 *crow::connections::systemBus, connectionName, path, 1698 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 1699 [asyncResp](const boost::system::error_code& ec, 1700 const std::string& property) { 1701 if (ec) 1702 { 1703 BMCWEB_LOG_DEBUG("DBUS response error for " 1704 "Location"); 1705 messages::internalError(asyncResp->res); 1706 return; 1707 } 1708 1709 asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 1710 property; 1711 }); 1712 } 1713 // avoid name collision systems.hpp 1714 inline void 1715 managerGetLastResetTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1716 { 1717 BMCWEB_LOG_DEBUG("Getting Manager Last Reset Time"); 1718 1719 sdbusplus::asio::getProperty<uint64_t>( 1720 *crow::connections::systemBus, "xyz.openbmc_project.State.BMC", 1721 "/xyz/openbmc_project/state/bmc0", "xyz.openbmc_project.State.BMC", 1722 "LastRebootTime", 1723 [asyncResp](const boost::system::error_code& ec, 1724 const uint64_t lastResetTime) { 1725 if (ec) 1726 { 1727 BMCWEB_LOG_DEBUG("D-BUS response error {}", ec); 1728 return; 1729 } 1730 1731 // LastRebootTime is epoch time, in milliseconds 1732 // https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19 1733 uint64_t lastResetTimeStamp = lastResetTime / 1000; 1734 1735 // Convert to ISO 8601 standard 1736 asyncResp->res.jsonValue["LastResetTime"] = 1737 redfish::time_utils::getDateTimeUint(lastResetTimeStamp); 1738 }); 1739 } 1740 1741 /** 1742 * @brief Set the running firmware image 1743 * 1744 * @param[i,o] asyncResp - Async response object 1745 * @param[i] runningFirmwareTarget - Image to make the running image 1746 * 1747 * @return void 1748 */ 1749 inline void 1750 setActiveFirmwareImage(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1751 const std::string& runningFirmwareTarget) 1752 { 1753 // Get the Id from /redfish/v1/UpdateService/FirmwareInventory/<Id> 1754 std::string::size_type idPos = runningFirmwareTarget.rfind('/'); 1755 if (idPos == std::string::npos) 1756 { 1757 messages::propertyValueNotInList(asyncResp->res, runningFirmwareTarget, 1758 "@odata.id"); 1759 BMCWEB_LOG_DEBUG("Can't parse firmware ID!"); 1760 return; 1761 } 1762 idPos++; 1763 if (idPos >= runningFirmwareTarget.size()) 1764 { 1765 messages::propertyValueNotInList(asyncResp->res, runningFirmwareTarget, 1766 "@odata.id"); 1767 BMCWEB_LOG_DEBUG("Invalid firmware ID."); 1768 return; 1769 } 1770 std::string firmwareId = runningFirmwareTarget.substr(idPos); 1771 1772 // Make sure the image is valid before setting priority 1773 sdbusplus::message::object_path objPath("/xyz/openbmc_project/software"); 1774 dbus::utility::getManagedObjects( 1775 "xyz.openbmc_project.Software.BMC.Updater", objPath, 1776 [asyncResp, firmwareId, runningFirmwareTarget]( 1777 const boost::system::error_code& ec, 1778 const dbus::utility::ManagedObjectType& subtree) { 1779 if (ec) 1780 { 1781 BMCWEB_LOG_DEBUG("D-Bus response error getting objects."); 1782 messages::internalError(asyncResp->res); 1783 return; 1784 } 1785 1786 if (subtree.empty()) 1787 { 1788 BMCWEB_LOG_DEBUG("Can't find image!"); 1789 messages::internalError(asyncResp->res); 1790 return; 1791 } 1792 1793 bool foundImage = false; 1794 for (const auto& object : subtree) 1795 { 1796 const std::string& path = 1797 static_cast<const std::string&>(object.first); 1798 std::size_t idPos2 = path.rfind('/'); 1799 1800 if (idPos2 == std::string::npos) 1801 { 1802 continue; 1803 } 1804 1805 idPos2++; 1806 if (idPos2 >= path.size()) 1807 { 1808 continue; 1809 } 1810 1811 if (path.substr(idPos2) == firmwareId) 1812 { 1813 foundImage = true; 1814 break; 1815 } 1816 } 1817 1818 if (!foundImage) 1819 { 1820 messages::propertyValueNotInList( 1821 asyncResp->res, runningFirmwareTarget, "@odata.id"); 1822 BMCWEB_LOG_DEBUG("Invalid firmware ID."); 1823 return; 1824 } 1825 1826 BMCWEB_LOG_DEBUG("Setting firmware version {} to priority 0.", 1827 firmwareId); 1828 1829 // Only support Immediate 1830 // An addition could be a Redfish Setting like 1831 // ActiveSoftwareImageApplyTime and support OnReset 1832 sdbusplus::asio::setProperty( 1833 *crow::connections::systemBus, 1834 "xyz.openbmc_project.Software.BMC.Updater", 1835 "/xyz/openbmc_project/software/" + firmwareId, 1836 "xyz.openbmc_project.Software.RedundancyPriority", "Priority", 1837 static_cast<uint8_t>(0), 1838 [asyncResp](const boost::system::error_code& ec2) { 1839 if (ec2) 1840 { 1841 BMCWEB_LOG_DEBUG("D-Bus response error setting."); 1842 messages::internalError(asyncResp->res); 1843 return; 1844 } 1845 doBMCGracefulRestart(asyncResp); 1846 }); 1847 }); 1848 } 1849 1850 inline void setDateTime(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 1851 std::string datetime) 1852 { 1853 BMCWEB_LOG_DEBUG("Set date time: {}", datetime); 1854 1855 std::optional<redfish::time_utils::usSinceEpoch> us = 1856 redfish::time_utils::dateStringToEpoch(datetime); 1857 if (!us) 1858 { 1859 messages::propertyValueFormatError(asyncResp->res, datetime, 1860 "DateTime"); 1861 return; 1862 } 1863 sdbusplus::asio::setProperty( 1864 *crow::connections::systemBus, "xyz.openbmc_project.Time.Manager", 1865 "/xyz/openbmc_project/time/bmc", "xyz.openbmc_project.Time.EpochTime", 1866 "Elapsed", us->count(), 1867 [asyncResp{std::move(asyncResp)}, 1868 datetime{std::move(datetime)}](const boost::system::error_code& ec) { 1869 if (ec) 1870 { 1871 BMCWEB_LOG_DEBUG("Failed to set elapsed time. " 1872 "DBUS response error {}", 1873 ec); 1874 messages::internalError(asyncResp->res); 1875 return; 1876 } 1877 asyncResp->res.jsonValue["DateTime"] = datetime; 1878 }); 1879 } 1880 1881 inline void 1882 checkForQuiesced(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1883 { 1884 sdbusplus::asio::getProperty<std::string>( 1885 *crow::connections::systemBus, "org.freedesktop.systemd1", 1886 "/org/freedesktop/systemd1/unit/obmc-bmc-service-quiesce@0.target", 1887 "org.freedesktop.systemd1.Unit", "ActiveState", 1888 [asyncResp](const boost::system::error_code& ec, 1889 const std::string& val) { 1890 if (!ec) 1891 { 1892 if (val == "active") 1893 { 1894 asyncResp->res.jsonValue["Status"]["Health"] = "Critical"; 1895 asyncResp->res.jsonValue["Status"]["State"] = "Quiesced"; 1896 return; 1897 } 1898 } 1899 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 1900 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 1901 }); 1902 } 1903 1904 inline void requestRoutesManager(App& app) 1905 { 1906 std::string uuid = persistent_data::getConfig().systemUuid; 1907 1908 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/") 1909 .privileges(redfish::privileges::getManager) 1910 .methods(boost::beast::http::verb::get)( 1911 [&app, uuid](const crow::Request& req, 1912 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1913 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1914 { 1915 return; 1916 } 1917 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc"; 1918 asyncResp->res.jsonValue["@odata.type"] = "#Manager.v1_14_0.Manager"; 1919 asyncResp->res.jsonValue["Id"] = "bmc"; 1920 asyncResp->res.jsonValue["Name"] = "OpenBmc Manager"; 1921 asyncResp->res.jsonValue["Description"] = 1922 "Baseboard Management Controller"; 1923 asyncResp->res.jsonValue["PowerState"] = "On"; 1924 1925 asyncResp->res.jsonValue["ManagerType"] = "BMC"; 1926 asyncResp->res.jsonValue["UUID"] = systemd_utils::getUuid(); 1927 asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid; 1928 asyncResp->res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model 1929 1930 asyncResp->res.jsonValue["LogServices"]["@odata.id"] = 1931 "/redfish/v1/Managers/bmc/LogServices"; 1932 asyncResp->res.jsonValue["NetworkProtocol"]["@odata.id"] = 1933 "/redfish/v1/Managers/bmc/NetworkProtocol"; 1934 asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = 1935 "/redfish/v1/Managers/bmc/EthernetInterfaces"; 1936 1937 #ifdef BMCWEB_ENABLE_VM_NBDPROXY 1938 asyncResp->res.jsonValue["VirtualMedia"]["@odata.id"] = 1939 "/redfish/v1/Managers/bmc/VirtualMedia"; 1940 #endif // BMCWEB_ENABLE_VM_NBDPROXY 1941 1942 // default oem data 1943 nlohmann::json& oem = asyncResp->res.jsonValue["Oem"]; 1944 nlohmann::json& oemOpenbmc = oem["OpenBmc"]; 1945 oem["@odata.type"] = "#OemManager.Oem"; 1946 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem"; 1947 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc"; 1948 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc"; 1949 1950 nlohmann::json::object_t certificates; 1951 certificates["@odata.id"] = 1952 "/redfish/v1/Managers/bmc/Truststore/Certificates"; 1953 oemOpenbmc["Certificates"] = std::move(certificates); 1954 1955 // Manager.Reset (an action) can be many values, OpenBMC only 1956 // supports BMC reboot. 1957 nlohmann::json& managerReset = 1958 asyncResp->res.jsonValue["Actions"]["#Manager.Reset"]; 1959 managerReset["target"] = 1960 "/redfish/v1/Managers/bmc/Actions/Manager.Reset"; 1961 managerReset["@Redfish.ActionInfo"] = 1962 "/redfish/v1/Managers/bmc/ResetActionInfo"; 1963 1964 // ResetToDefaults (Factory Reset) has values like 1965 // PreserveNetworkAndUsers and PreserveNetwork that aren't supported 1966 // on OpenBMC 1967 nlohmann::json& resetToDefaults = 1968 asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"]; 1969 resetToDefaults["target"] = 1970 "/redfish/v1/Managers/bmc/Actions/Manager.ResetToDefaults"; 1971 resetToDefaults["ResetType@Redfish.AllowableValues"] = 1972 nlohmann::json::array_t({"ResetAll"}); 1973 1974 std::pair<std::string, std::string> redfishDateTimeOffset = 1975 redfish::time_utils::getDateTimeOffsetNow(); 1976 1977 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 1978 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 1979 redfishDateTimeOffset.second; 1980 1981 // TODO (Gunnar): Remove these one day since moved to ComputerSystem 1982 // Still used by OCP profiles 1983 // https://github.com/opencomputeproject/OCP-Profiles/issues/23 1984 // Fill in SerialConsole info 1985 asyncResp->res.jsonValue["SerialConsole"]["ServiceEnabled"] = true; 1986 asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15; 1987 asyncResp->res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = 1988 nlohmann::json::array_t({"IPMI", "SSH"}); 1989 #ifdef BMCWEB_ENABLE_KVM 1990 // Fill in GraphicalConsole info 1991 asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true; 1992 asyncResp->res.jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 1993 4; 1994 asyncResp->res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = 1995 nlohmann::json::array_t({"KVMIP"}); 1996 #endif // BMCWEB_ENABLE_KVM 1997 if constexpr (!bmcwebEnableMultiHost) 1998 { 1999 asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = 2000 1; 2001 2002 nlohmann::json::array_t managerForServers; 2003 nlohmann::json::object_t manager; 2004 manager["@odata.id"] = "/redfish/v1/Systems/system"; 2005 managerForServers.emplace_back(std::move(manager)); 2006 2007 asyncResp->res.jsonValue["Links"]["ManagerForServers"] = 2008 std::move(managerForServers); 2009 } 2010 if constexpr (bmcwebEnableHealthPopulate) 2011 { 2012 auto health = std::make_shared<HealthPopulate>(asyncResp); 2013 health->isManagersHealth = true; 2014 health->populate(); 2015 } 2016 2017 sw_util::populateSoftwareInformation(asyncResp, sw_util::bmcPurpose, 2018 "FirmwareVersion", true); 2019 2020 managerGetLastResetTime(asyncResp); 2021 2022 // ManagerDiagnosticData is added for all BMCs. 2023 nlohmann::json& managerDiagnosticData = 2024 asyncResp->res.jsonValue["ManagerDiagnosticData"]; 2025 managerDiagnosticData["@odata.id"] = 2026 "/redfish/v1/Managers/bmc/ManagerDiagnosticData"; 2027 2028 #ifdef BMCWEB_ENABLE_REDFISH_OEM_MANAGER_FAN_DATA 2029 auto pids = std::make_shared<GetPIDValues>(asyncResp); 2030 pids->run(); 2031 #endif 2032 2033 getMainChassisId(asyncResp, 2034 [](const std::string& chassisId, 2035 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { 2036 aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1; 2037 nlohmann::json::array_t managerForChassis; 2038 nlohmann::json::object_t managerObj; 2039 boost::urls::url chassiUrl = 2040 boost::urls::format("/redfish/v1/Chassis/{}", chassisId); 2041 managerObj["@odata.id"] = chassiUrl; 2042 managerForChassis.emplace_back(std::move(managerObj)); 2043 aRsp->res.jsonValue["Links"]["ManagerForChassis"] = 2044 std::move(managerForChassis); 2045 aRsp->res.jsonValue["Links"]["ManagerInChassis"]["@odata.id"] = 2046 chassiUrl; 2047 }); 2048 2049 sdbusplus::asio::getProperty<double>( 2050 *crow::connections::systemBus, "org.freedesktop.systemd1", 2051 "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", 2052 "Progress", 2053 [asyncResp](const boost::system::error_code& ec, double val) { 2054 if (ec) 2055 { 2056 BMCWEB_LOG_ERROR("Error while getting progress"); 2057 messages::internalError(asyncResp->res); 2058 return; 2059 } 2060 if (val < 1.0) 2061 { 2062 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 2063 asyncResp->res.jsonValue["Status"]["State"] = "Starting"; 2064 return; 2065 } 2066 checkForQuiesced(asyncResp); 2067 }); 2068 2069 constexpr std::array<std::string_view, 1> interfaces = { 2070 "xyz.openbmc_project.Inventory.Item.Bmc"}; 2071 dbus::utility::getSubTree( 2072 "/xyz/openbmc_project/inventory", 0, interfaces, 2073 [asyncResp]( 2074 const boost::system::error_code& ec, 2075 const dbus::utility::MapperGetSubTreeResponse& subtree) { 2076 if (ec) 2077 { 2078 BMCWEB_LOG_DEBUG("D-Bus response error on GetSubTree {}", ec); 2079 return; 2080 } 2081 if (subtree.empty()) 2082 { 2083 BMCWEB_LOG_DEBUG("Can't find bmc D-Bus object!"); 2084 return; 2085 } 2086 // Assume only 1 bmc D-Bus object 2087 // Throw an error if there is more than 1 2088 if (subtree.size() > 1) 2089 { 2090 BMCWEB_LOG_DEBUG("Found more than 1 bmc D-Bus object!"); 2091 messages::internalError(asyncResp->res); 2092 return; 2093 } 2094 2095 if (subtree[0].first.empty() || subtree[0].second.size() != 1) 2096 { 2097 BMCWEB_LOG_DEBUG("Error getting bmc D-Bus object!"); 2098 messages::internalError(asyncResp->res); 2099 return; 2100 } 2101 2102 const std::string& path = subtree[0].first; 2103 const std::string& connectionName = subtree[0].second[0].first; 2104 2105 for (const auto& interfaceName : subtree[0].second[0].second) 2106 { 2107 if (interfaceName == 2108 "xyz.openbmc_project.Inventory.Decorator.Asset") 2109 { 2110 sdbusplus::asio::getAllProperties( 2111 *crow::connections::systemBus, connectionName, path, 2112 "xyz.openbmc_project.Inventory.Decorator.Asset", 2113 [asyncResp](const boost::system::error_code& ec2, 2114 const dbus::utility::DBusPropertiesMap& 2115 propertiesList) { 2116 if (ec2) 2117 { 2118 BMCWEB_LOG_DEBUG("Can't get bmc asset!"); 2119 return; 2120 } 2121 2122 const std::string* partNumber = nullptr; 2123 const std::string* serialNumber = nullptr; 2124 const std::string* manufacturer = nullptr; 2125 const std::string* model = nullptr; 2126 const std::string* sparePartNumber = nullptr; 2127 2128 const bool success = sdbusplus::unpackPropertiesNoThrow( 2129 dbus_utils::UnpackErrorPrinter(), propertiesList, 2130 "PartNumber", partNumber, "SerialNumber", 2131 serialNumber, "Manufacturer", manufacturer, "Model", 2132 model, "SparePartNumber", sparePartNumber); 2133 2134 if (!success) 2135 { 2136 messages::internalError(asyncResp->res); 2137 return; 2138 } 2139 2140 if (partNumber != nullptr) 2141 { 2142 asyncResp->res.jsonValue["PartNumber"] = 2143 *partNumber; 2144 } 2145 2146 if (serialNumber != nullptr) 2147 { 2148 asyncResp->res.jsonValue["SerialNumber"] = 2149 *serialNumber; 2150 } 2151 2152 if (manufacturer != nullptr) 2153 { 2154 asyncResp->res.jsonValue["Manufacturer"] = 2155 *manufacturer; 2156 } 2157 2158 if (model != nullptr) 2159 { 2160 asyncResp->res.jsonValue["Model"] = *model; 2161 } 2162 2163 if (sparePartNumber != nullptr) 2164 { 2165 asyncResp->res.jsonValue["SparePartNumber"] = 2166 *sparePartNumber; 2167 } 2168 }); 2169 } 2170 else if (interfaceName == 2171 "xyz.openbmc_project.Inventory.Decorator.LocationCode") 2172 { 2173 getLocation(asyncResp, connectionName, path); 2174 } 2175 } 2176 }); 2177 }); 2178 2179 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/") 2180 .privileges(redfish::privileges::patchManager) 2181 .methods(boost::beast::http::verb::patch)( 2182 [&app](const crow::Request& req, 2183 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2184 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2185 { 2186 return; 2187 } 2188 std::optional<nlohmann::json> oem; 2189 std::optional<nlohmann::json> links; 2190 std::optional<std::string> datetime; 2191 2192 if (!json_util::readJsonPatch(req, asyncResp->res, "Oem", oem, 2193 "DateTime", datetime, "Links", links)) 2194 { 2195 return; 2196 } 2197 2198 if (oem) 2199 { 2200 #ifdef BMCWEB_ENABLE_REDFISH_OEM_MANAGER_FAN_DATA 2201 std::optional<nlohmann::json> openbmc; 2202 if (!redfish::json_util::readJson(*oem, asyncResp->res, "OpenBmc", 2203 openbmc)) 2204 { 2205 return; 2206 } 2207 if (openbmc) 2208 { 2209 std::optional<nlohmann::json> fan; 2210 if (!redfish::json_util::readJson(*openbmc, asyncResp->res, 2211 "Fan", fan)) 2212 { 2213 return; 2214 } 2215 if (fan) 2216 { 2217 auto pid = std::make_shared<SetPIDValues>(asyncResp, *fan); 2218 pid->run(); 2219 } 2220 } 2221 #else 2222 messages::propertyUnknown(asyncResp->res, "Oem"); 2223 return; 2224 #endif 2225 } 2226 if (links) 2227 { 2228 std::optional<nlohmann::json> activeSoftwareImage; 2229 if (!redfish::json_util::readJson(*links, asyncResp->res, 2230 "ActiveSoftwareImage", 2231 activeSoftwareImage)) 2232 { 2233 return; 2234 } 2235 if (activeSoftwareImage) 2236 { 2237 std::optional<std::string> odataId; 2238 if (!json_util::readJson(*activeSoftwareImage, asyncResp->res, 2239 "@odata.id", odataId)) 2240 { 2241 return; 2242 } 2243 2244 if (odataId) 2245 { 2246 setActiveFirmwareImage(asyncResp, *odataId); 2247 } 2248 } 2249 } 2250 if (datetime) 2251 { 2252 setDateTime(asyncResp, std::move(*datetime)); 2253 } 2254 }); 2255 } 2256 2257 inline void requestRoutesManagerCollection(App& app) 2258 { 2259 BMCWEB_ROUTE(app, "/redfish/v1/Managers/") 2260 .privileges(redfish::privileges::getManagerCollection) 2261 .methods(boost::beast::http::verb::get)( 2262 [&app](const crow::Request& req, 2263 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2264 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2265 { 2266 return; 2267 } 2268 // Collections don't include the static data added by SubRoute 2269 // because it has a duplicate entry for members 2270 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers"; 2271 asyncResp->res.jsonValue["@odata.type"] = 2272 "#ManagerCollection.ManagerCollection"; 2273 asyncResp->res.jsonValue["Name"] = "Manager Collection"; 2274 asyncResp->res.jsonValue["Members@odata.count"] = 1; 2275 nlohmann::json::array_t members; 2276 nlohmann::json& bmc = members.emplace_back(); 2277 bmc["@odata.id"] = "/redfish/v1/Managers/bmc"; 2278 asyncResp->res.jsonValue["Members"] = std::move(members); 2279 }); 2280 } 2281 } // namespace redfish 2282