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 <sstream> 42 #include <string_view> 43 #include <variant> 44 45 namespace redfish 46 { 47 48 /** 49 * Function reboots the BMC. 50 * 51 * @param[in] asyncResp - Shared pointer for completing asynchronous calls 52 */ 53 inline void 54 doBMCGracefulRestart(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 55 { 56 const char* processName = "xyz.openbmc_project.State.BMC"; 57 const char* objectPath = "/xyz/openbmc_project/state/bmc0"; 58 const char* interfaceName = "xyz.openbmc_project.State.BMC"; 59 const std::string& propertyValue = 60 "xyz.openbmc_project.State.BMC.Transition.Reboot"; 61 const char* destProperty = "RequestedBMCTransition"; 62 63 // Create the D-Bus variant for D-Bus call. 64 sdbusplus::asio::setProperty( 65 *crow::connections::systemBus, processName, objectPath, interfaceName, 66 destProperty, propertyValue, 67 [asyncResp](const boost::system::error_code& ec) { 68 // Use "Set" method to set the property value. 69 if (ec) 70 { 71 BMCWEB_LOG_DEBUG("[Set] Bad D-Bus request error: {}", ec); 72 messages::internalError(asyncResp->res); 73 return; 74 } 75 76 messages::success(asyncResp->res); 77 }); 78 } 79 80 inline void 81 doBMCForceRestart(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 82 { 83 const char* processName = "xyz.openbmc_project.State.BMC"; 84 const char* objectPath = "/xyz/openbmc_project/state/bmc0"; 85 const char* interfaceName = "xyz.openbmc_project.State.BMC"; 86 const std::string& propertyValue = 87 "xyz.openbmc_project.State.BMC.Transition.HardReboot"; 88 const char* destProperty = "RequestedBMCTransition"; 89 90 // Create the D-Bus variant for D-Bus call. 91 sdbusplus::asio::setProperty( 92 *crow::connections::systemBus, processName, objectPath, interfaceName, 93 destProperty, propertyValue, 94 [asyncResp](const boost::system::error_code& ec) { 95 // Use "Set" method to set the property value. 96 if (ec) 97 { 98 BMCWEB_LOG_DEBUG("[Set] Bad D-Bus request error: {}", ec); 99 messages::internalError(asyncResp->res); 100 return; 101 } 102 103 messages::success(asyncResp->res); 104 }); 105 } 106 107 /** 108 * ManagerResetAction class supports the POST method for the Reset (reboot) 109 * action. 110 */ 111 inline void requestRoutesManagerResetAction(App& app) 112 { 113 /** 114 * Function handles POST method request. 115 * Analyzes POST body before sending Reset (Reboot) request data to D-Bus. 116 * OpenBMC supports ResetType "GracefulRestart" and "ForceRestart". 117 */ 118 119 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Actions/Manager.Reset/") 120 .privileges(redfish::privileges::postManager) 121 .methods(boost::beast::http::verb::post)( 122 [&app](const crow::Request& req, 123 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 124 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 125 { 126 return; 127 } 128 BMCWEB_LOG_DEBUG("Post Manager Reset."); 129 130 std::string resetType; 131 132 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", 133 resetType)) 134 { 135 return; 136 } 137 138 if (resetType == "GracefulRestart") 139 { 140 BMCWEB_LOG_DEBUG("Proceeding with {}", resetType); 141 doBMCGracefulRestart(asyncResp); 142 return; 143 } 144 if (resetType == "ForceRestart") 145 { 146 BMCWEB_LOG_DEBUG("Proceeding with {}", resetType); 147 doBMCForceRestart(asyncResp); 148 return; 149 } 150 BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", resetType); 151 messages::actionParameterNotSupported(asyncResp->res, resetType, 152 "ResetType"); 153 154 return; 155 }); 156 } 157 158 /** 159 * ManagerResetToDefaultsAction class supports POST method for factory reset 160 * action. 161 */ 162 inline void requestRoutesManagerResetToDefaultsAction(App& app) 163 { 164 /** 165 * Function handles ResetToDefaults POST method request. 166 * 167 * Analyzes POST body message and factory resets BMC by calling 168 * BMC code updater factory reset followed by a BMC reboot. 169 * 170 * BMC code updater factory reset wipes the whole BMC read-write 171 * filesystem which includes things like the network settings. 172 * 173 * OpenBMC only supports ResetToDefaultsType "ResetAll". 174 */ 175 176 BMCWEB_ROUTE(app, 177 "/redfish/v1/Managers/bmc/Actions/Manager.ResetToDefaults/") 178 .privileges(redfish::privileges::postManager) 179 .methods(boost::beast::http::verb::post)( 180 [&app](const crow::Request& req, 181 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 182 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 183 { 184 return; 185 } 186 BMCWEB_LOG_DEBUG("Post ResetToDefaults."); 187 188 std::string resetType; 189 190 if (!json_util::readJsonAction(req, asyncResp->res, 191 "ResetToDefaultsType", resetType)) 192 { 193 BMCWEB_LOG_DEBUG("Missing property ResetToDefaultsType."); 194 195 messages::actionParameterMissing(asyncResp->res, "ResetToDefaults", 196 "ResetToDefaultsType"); 197 return; 198 } 199 200 if (resetType != "ResetAll") 201 { 202 BMCWEB_LOG_DEBUG( 203 "Invalid property value for ResetToDefaultsType: {}", 204 resetType); 205 messages::actionParameterNotSupported(asyncResp->res, resetType, 206 "ResetToDefaultsType"); 207 return; 208 } 209 210 crow::connections::systemBus->async_method_call( 211 [asyncResp](const boost::system::error_code& ec) { 212 if (ec) 213 { 214 BMCWEB_LOG_DEBUG("Failed to ResetToDefaults: {}", ec); 215 messages::internalError(asyncResp->res); 216 return; 217 } 218 // Factory Reset doesn't actually happen until a reboot 219 // Can't erase what the BMC is running on 220 doBMCGracefulRestart(asyncResp); 221 }, 222 "xyz.openbmc_project.Software.BMC.Updater", 223 "/xyz/openbmc_project/software", 224 "xyz.openbmc_project.Common.FactoryReset", "Reset"); 225 }); 226 } 227 228 /** 229 * ManagerResetActionInfo derived class for delivering Manager 230 * ResetType AllowableValues using ResetInfo schema. 231 */ 232 inline void requestRoutesManagerResetActionInfo(App& app) 233 { 234 /** 235 * Functions triggers appropriate requests on DBus 236 */ 237 238 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/ResetActionInfo/") 239 .privileges(redfish::privileges::getActionInfo) 240 .methods(boost::beast::http::verb::get)( 241 [&app](const crow::Request& req, 242 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 243 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 244 { 245 return; 246 } 247 248 asyncResp->res.jsonValue["@odata.type"] = 249 "#ActionInfo.v1_1_2.ActionInfo"; 250 asyncResp->res.jsonValue["@odata.id"] = 251 "/redfish/v1/Managers/bmc/ResetActionInfo"; 252 asyncResp->res.jsonValue["Name"] = "Reset Action Info"; 253 asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; 254 nlohmann::json::object_t parameter; 255 parameter["Name"] = "ResetType"; 256 parameter["Required"] = true; 257 parameter["DataType"] = "String"; 258 259 nlohmann::json::array_t allowableValues; 260 allowableValues.emplace_back("GracefulRestart"); 261 allowableValues.emplace_back("ForceRestart"); 262 parameter["AllowableValues"] = std::move(allowableValues); 263 264 nlohmann::json::array_t parameters; 265 parameters.emplace_back(std::move(parameter)); 266 267 asyncResp->res.jsonValue["Parameters"] = std::move(parameters); 268 }); 269 } 270 271 static constexpr const char* objectManagerIface = 272 "org.freedesktop.DBus.ObjectManager"; 273 static constexpr const char* pidConfigurationIface = 274 "xyz.openbmc_project.Configuration.Pid"; 275 static constexpr const char* pidZoneConfigurationIface = 276 "xyz.openbmc_project.Configuration.Pid.Zone"; 277 static constexpr const char* stepwiseConfigurationIface = 278 "xyz.openbmc_project.Configuration.Stepwise"; 279 static constexpr const char* thermalModeIface = 280 "xyz.openbmc_project.Control.ThermalMode"; 281 282 inline void 283 asyncPopulatePid(const std::string& connection, const std::string& path, 284 const std::string& currentProfile, 285 const std::vector<std::string>& supportedProfiles, 286 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 287 { 288 sdbusplus::message::object_path objPath(path); 289 dbus::utility::getManagedObjects( 290 connection, objPath, 291 [asyncResp, currentProfile, supportedProfiles]( 292 const boost::system::error_code& ec, 293 const dbus::utility::ManagedObjectType& managedObj) { 294 if (ec) 295 { 296 BMCWEB_LOG_ERROR("{}", ec); 297 messages::internalError(asyncResp->res); 298 return; 299 } 300 nlohmann::json& configRoot = 301 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"]; 302 nlohmann::json& fans = configRoot["FanControllers"]; 303 fans["@odata.type"] = "#OemManager.FanControllers"; 304 fans["@odata.id"] = 305 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanControllers"; 306 307 nlohmann::json& pids = configRoot["PidControllers"]; 308 pids["@odata.type"] = "#OemManager.PidControllers"; 309 pids["@odata.id"] = 310 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers"; 311 312 nlohmann::json& stepwise = configRoot["StepwiseControllers"]; 313 stepwise["@odata.type"] = "#OemManager.StepwiseControllers"; 314 stepwise["@odata.id"] = 315 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/StepwiseControllers"; 316 317 nlohmann::json& zones = configRoot["FanZones"]; 318 zones["@odata.id"] = 319 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones"; 320 zones["@odata.type"] = "#OemManager.FanZones"; 321 configRoot["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan"; 322 configRoot["@odata.type"] = "#OemManager.Fan"; 323 configRoot["Profile@Redfish.AllowableValues"] = supportedProfiles; 324 325 if (!currentProfile.empty()) 326 { 327 configRoot["Profile"] = currentProfile; 328 } 329 BMCWEB_LOG_DEBUG("profile = {} !", currentProfile); 330 331 for (const auto& pathPair : managedObj) 332 { 333 for (const auto& intfPair : pathPair.second) 334 { 335 if (intfPair.first != pidConfigurationIface && 336 intfPair.first != pidZoneConfigurationIface && 337 intfPair.first != stepwiseConfigurationIface) 338 { 339 continue; 340 } 341 342 std::string name; 343 344 for (const std::pair<std::string, 345 dbus::utility::DbusVariantType>& propPair : 346 intfPair.second) 347 { 348 if (propPair.first == "Name") 349 { 350 const std::string* namePtr = 351 std::get_if<std::string>(&propPair.second); 352 if (namePtr == nullptr) 353 { 354 BMCWEB_LOG_ERROR("Pid Name Field illegal"); 355 messages::internalError(asyncResp->res); 356 return; 357 } 358 name = *namePtr; 359 dbus::utility::escapePathForDbus(name); 360 } 361 else if (propPair.first == "Profiles") 362 { 363 const std::vector<std::string>* profiles = 364 std::get_if<std::vector<std::string>>( 365 &propPair.second); 366 if (profiles == nullptr) 367 { 368 BMCWEB_LOG_ERROR("Pid Profiles Field illegal"); 369 messages::internalError(asyncResp->res); 370 return; 371 } 372 if (std::find(profiles->begin(), profiles->end(), 373 currentProfile) == profiles->end()) 374 { 375 BMCWEB_LOG_INFO( 376 "{} not supported in current profile", name); 377 continue; 378 } 379 } 380 } 381 nlohmann::json* config = nullptr; 382 const std::string* classPtr = nullptr; 383 384 for (const std::pair<std::string, 385 dbus::utility::DbusVariantType>& propPair : 386 intfPair.second) 387 { 388 if (propPair.first == "Class") 389 { 390 classPtr = std::get_if<std::string>(&propPair.second); 391 } 392 } 393 394 boost::urls::url url("/redfish/v1/Managers/bmc"); 395 if (intfPair.first == pidZoneConfigurationIface) 396 { 397 std::string chassis; 398 if (!dbus::utility::getNthStringFromPath(pathPair.first.str, 399 5, chassis)) 400 { 401 chassis = "#IllegalValue"; 402 } 403 nlohmann::json& zone = zones[name]; 404 zone["Chassis"]["@odata.id"] = 405 boost::urls::format("/redfish/v1/Chassis/{}", chassis); 406 url.set_fragment( 407 ("/Oem/OpenBmc/Fan/FanZones"_json_pointer / name) 408 .to_string()); 409 zone["@odata.id"] = std::move(url); 410 zone["@odata.type"] = "#OemManager.FanZone"; 411 config = &zone; 412 } 413 414 else if (intfPair.first == stepwiseConfigurationIface) 415 { 416 if (classPtr == nullptr) 417 { 418 BMCWEB_LOG_ERROR("Pid Class Field illegal"); 419 messages::internalError(asyncResp->res); 420 return; 421 } 422 423 nlohmann::json& controller = stepwise[name]; 424 config = &controller; 425 url.set_fragment( 426 ("/Oem/OpenBmc/Fan/StepwiseControllers"_json_pointer / 427 name) 428 .to_string()); 429 controller["@odata.id"] = std::move(url); 430 controller["@odata.type"] = 431 "#OemManager.StepwiseController"; 432 433 controller["Direction"] = *classPtr; 434 } 435 436 // pid and fans are off the same configuration 437 else if (intfPair.first == pidConfigurationIface) 438 { 439 if (classPtr == nullptr) 440 { 441 BMCWEB_LOG_ERROR("Pid Class Field illegal"); 442 messages::internalError(asyncResp->res); 443 return; 444 } 445 bool isFan = *classPtr == "fan"; 446 nlohmann::json& element = isFan ? fans[name] : pids[name]; 447 config = &element; 448 if (isFan) 449 { 450 url.set_fragment( 451 ("/Oem/OpenBmc/Fan/FanControllers"_json_pointer / 452 name) 453 .to_string()); 454 element["@odata.id"] = std::move(url); 455 element["@odata.type"] = "#OemManager.FanController"; 456 } 457 else 458 { 459 url.set_fragment( 460 ("/Oem/OpenBmc/Fan/PidControllers"_json_pointer / 461 name) 462 .to_string()); 463 element["@odata.id"] = std::move(url); 464 element["@odata.type"] = "#OemManager.PidController"; 465 } 466 } 467 else 468 { 469 BMCWEB_LOG_ERROR("Unexpected configuration"); 470 messages::internalError(asyncResp->res); 471 return; 472 } 473 474 // used for making maps out of 2 vectors 475 const std::vector<double>* keys = nullptr; 476 const std::vector<double>* values = nullptr; 477 478 for (const auto& propertyPair : intfPair.second) 479 { 480 if (propertyPair.first == "Type" || 481 propertyPair.first == "Class" || 482 propertyPair.first == "Name") 483 { 484 continue; 485 } 486 487 // zones 488 if (intfPair.first == pidZoneConfigurationIface) 489 { 490 const double* ptr = 491 std::get_if<double>(&propertyPair.second); 492 if (ptr == nullptr) 493 { 494 BMCWEB_LOG_ERROR("Field Illegal {}", 495 propertyPair.first); 496 messages::internalError(asyncResp->res); 497 return; 498 } 499 (*config)[propertyPair.first] = *ptr; 500 } 501 502 if (intfPair.first == stepwiseConfigurationIface) 503 { 504 if (propertyPair.first == "Reading" || 505 propertyPair.first == "Output") 506 { 507 const std::vector<double>* ptr = 508 std::get_if<std::vector<double>>( 509 &propertyPair.second); 510 511 if (ptr == nullptr) 512 { 513 BMCWEB_LOG_ERROR("Field Illegal {}", 514 propertyPair.first); 515 messages::internalError(asyncResp->res); 516 return; 517 } 518 519 if (propertyPair.first == "Reading") 520 { 521 keys = ptr; 522 } 523 else 524 { 525 values = ptr; 526 } 527 if (keys != nullptr && values != nullptr) 528 { 529 if (keys->size() != values->size()) 530 { 531 BMCWEB_LOG_ERROR( 532 "Reading and Output size don't match "); 533 messages::internalError(asyncResp->res); 534 return; 535 } 536 nlohmann::json& steps = (*config)["Steps"]; 537 steps = nlohmann::json::array(); 538 for (size_t ii = 0; ii < keys->size(); ii++) 539 { 540 nlohmann::json::object_t step; 541 step["Target"] = (*keys)[ii]; 542 step["Output"] = (*values)[ii]; 543 steps.emplace_back(std::move(step)); 544 } 545 } 546 } 547 if (propertyPair.first == "NegativeHysteresis" || 548 propertyPair.first == "PositiveHysteresis") 549 { 550 const double* ptr = 551 std::get_if<double>(&propertyPair.second); 552 if (ptr == nullptr) 553 { 554 BMCWEB_LOG_ERROR("Field Illegal {}", 555 propertyPair.first); 556 messages::internalError(asyncResp->res); 557 return; 558 } 559 (*config)[propertyPair.first] = *ptr; 560 } 561 } 562 563 // pid and fans are off the same configuration 564 if (intfPair.first == pidConfigurationIface || 565 intfPair.first == stepwiseConfigurationIface) 566 { 567 if (propertyPair.first == "Zones") 568 { 569 const std::vector<std::string>* inputs = 570 std::get_if<std::vector<std::string>>( 571 &propertyPair.second); 572 573 if (inputs == nullptr) 574 { 575 BMCWEB_LOG_ERROR("Zones Pid Field Illegal"); 576 messages::internalError(asyncResp->res); 577 return; 578 } 579 auto& data = (*config)[propertyPair.first]; 580 data = nlohmann::json::array(); 581 for (std::string itemCopy : *inputs) 582 { 583 dbus::utility::escapePathForDbus(itemCopy); 584 nlohmann::json::object_t input; 585 boost::urls::url managerUrl = boost::urls::format( 586 "/redfish/v1/Managers/bmc#{}", 587 ("/Oem/OpenBmc/Fan/FanZones"_json_pointer / 588 itemCopy) 589 .to_string()); 590 input["@odata.id"] = std::move(managerUrl); 591 data.emplace_back(std::move(input)); 592 } 593 } 594 // todo(james): may never happen, but this 595 // assumes configuration data referenced in the 596 // PID config is provided by the same daemon, we 597 // could add another loop to cover all cases, 598 // but I'm okay kicking this can down the road a 599 // bit 600 601 else if (propertyPair.first == "Inputs" || 602 propertyPair.first == "Outputs") 603 { 604 auto& data = (*config)[propertyPair.first]; 605 const std::vector<std::string>* inputs = 606 std::get_if<std::vector<std::string>>( 607 &propertyPair.second); 608 609 if (inputs == nullptr) 610 { 611 BMCWEB_LOG_ERROR("Field Illegal {}", 612 propertyPair.first); 613 messages::internalError(asyncResp->res); 614 return; 615 } 616 data = *inputs; 617 } 618 else if (propertyPair.first == "SetPointOffset") 619 { 620 const std::string* ptr = 621 std::get_if<std::string>(&propertyPair.second); 622 623 if (ptr == nullptr) 624 { 625 BMCWEB_LOG_ERROR("Field Illegal {}", 626 propertyPair.first); 627 messages::internalError(asyncResp->res); 628 return; 629 } 630 // translate from dbus to redfish 631 if (*ptr == "WarningHigh") 632 { 633 (*config)["SetPointOffset"] = 634 "UpperThresholdNonCritical"; 635 } 636 else if (*ptr == "WarningLow") 637 { 638 (*config)["SetPointOffset"] = 639 "LowerThresholdNonCritical"; 640 } 641 else if (*ptr == "CriticalHigh") 642 { 643 (*config)["SetPointOffset"] = 644 "UpperThresholdCritical"; 645 } 646 else if (*ptr == "CriticalLow") 647 { 648 (*config)["SetPointOffset"] = 649 "LowerThresholdCritical"; 650 } 651 else 652 { 653 BMCWEB_LOG_ERROR("Value Illegal {}", *ptr); 654 messages::internalError(asyncResp->res); 655 return; 656 } 657 } 658 // doubles 659 else if (propertyPair.first == "FFGainCoefficient" || 660 propertyPair.first == "FFOffCoefficient" || 661 propertyPair.first == "ICoefficient" || 662 propertyPair.first == "ILimitMax" || 663 propertyPair.first == "ILimitMin" || 664 propertyPair.first == "PositiveHysteresis" || 665 propertyPair.first == "NegativeHysteresis" || 666 propertyPair.first == "OutLimitMax" || 667 propertyPair.first == "OutLimitMin" || 668 propertyPair.first == "PCoefficient" || 669 propertyPair.first == "SetPoint" || 670 propertyPair.first == "SlewNeg" || 671 propertyPair.first == "SlewPos") 672 { 673 const double* ptr = 674 std::get_if<double>(&propertyPair.second); 675 if (ptr == nullptr) 676 { 677 BMCWEB_LOG_ERROR("Field Illegal {}", 678 propertyPair.first); 679 messages::internalError(asyncResp->res); 680 return; 681 } 682 (*config)[propertyPair.first] = *ptr; 683 } 684 } 685 } 686 } 687 } 688 }); 689 } 690 691 enum class CreatePIDRet 692 { 693 fail, 694 del, 695 patch 696 }; 697 698 inline bool 699 getZonesFromJsonReq(const std::shared_ptr<bmcweb::AsyncResp>& response, 700 std::vector<nlohmann::json>& config, 701 std::vector<std::string>& zones) 702 { 703 if (config.empty()) 704 { 705 BMCWEB_LOG_ERROR("Empty Zones"); 706 messages::propertyValueFormatError(response->res, config, "Zones"); 707 return false; 708 } 709 for (auto& odata : config) 710 { 711 std::string path; 712 if (!redfish::json_util::readJson(odata, response->res, "@odata.id", 713 path)) 714 { 715 return false; 716 } 717 std::string input; 718 719 // 8 below comes from 720 // /redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/Left 721 // 0 1 2 3 4 5 6 7 8 722 if (!dbus::utility::getNthStringFromPath(path, 8, input)) 723 { 724 BMCWEB_LOG_ERROR("Got invalid path {}", path); 725 BMCWEB_LOG_ERROR("Illegal Type Zones"); 726 messages::propertyValueFormatError(response->res, odata, "Zones"); 727 return false; 728 } 729 std::replace(input.begin(), input.end(), '_', ' '); 730 zones.emplace_back(std::move(input)); 731 } 732 return true; 733 } 734 735 inline const dbus::utility::ManagedObjectType::value_type* 736 findChassis(const dbus::utility::ManagedObjectType& managedObj, 737 const std::string& value, std::string& chassis) 738 { 739 BMCWEB_LOG_DEBUG("Find Chassis: {}", value); 740 741 std::string escaped = value; 742 std::replace(escaped.begin(), escaped.end(), ' ', '_'); 743 escaped = "/" + escaped; 744 auto it = std::find_if(managedObj.begin(), managedObj.end(), 745 [&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::find(allowedDirections.begin(), allowedDirections.end(), 1122 *direction) == 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::find(configurations.begin(), configurations.end(), 1376 interface) != 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::find(supportedProfiles.begin(), supportedProfiles.end(), 1455 *profile) == 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::find_if(managedObj.begin(), 1494 managedObj.end(), 1495 [&dbusObjName](const auto& obj) { 1496 return boost::algorithm::ends_with(obj.first.str, 1497 "/" + dbusObjName); 1498 }); 1499 dbus::utility::DBusPropertiesMap output; 1500 1501 output.reserve(16); // The pid interface length 1502 1503 // determines if we're patching entity-manager or 1504 // creating a new object 1505 bool createNewObject = (pathItr == managedObj.end()); 1506 BMCWEB_LOG_DEBUG("Found = {}", !createNewObject); 1507 1508 std::string iface; 1509 if (!createNewObject) 1510 { 1511 bool findInterface = false; 1512 for (const auto& interface : pathItr->second) 1513 { 1514 if (interface.first == pidConfigurationIface) 1515 { 1516 if (type == "PidControllers" || 1517 type == "FanControllers") 1518 { 1519 iface = pidConfigurationIface; 1520 findInterface = true; 1521 break; 1522 } 1523 } 1524 else if (interface.first == pidZoneConfigurationIface) 1525 { 1526 if (type == "FanZones") 1527 { 1528 iface = pidConfigurationIface; 1529 findInterface = true; 1530 break; 1531 } 1532 } 1533 else if (interface.first == stepwiseConfigurationIface) 1534 { 1535 if (type == "StepwiseControllers") 1536 { 1537 iface = stepwiseConfigurationIface; 1538 findInterface = true; 1539 break; 1540 } 1541 } 1542 } 1543 1544 // create new object if interface not found 1545 if (!findInterface) 1546 { 1547 createNewObject = true; 1548 } 1549 } 1550 1551 if (createNewObject && it.value() == nullptr) 1552 { 1553 // can't delete a non-existent object 1554 messages::propertyValueNotInList(response->res, it.value(), 1555 name); 1556 continue; 1557 } 1558 1559 std::string path; 1560 if (pathItr != managedObj.end()) 1561 { 1562 path = pathItr->first.str; 1563 } 1564 1565 BMCWEB_LOG_DEBUG("Create new = {}", createNewObject); 1566 1567 // arbitrary limit to avoid attacks 1568 constexpr const size_t controllerLimit = 500; 1569 if (createNewObject && objectCount >= controllerLimit) 1570 { 1571 messages::resourceExhaustion(response->res, type); 1572 continue; 1573 } 1574 std::string escaped = name; 1575 std::replace(escaped.begin(), escaped.end(), '_', ' '); 1576 output.emplace_back("Name", escaped); 1577 1578 std::string chassis; 1579 CreatePIDRet ret = createPidInterface( 1580 response, type, it, path, managedObj, createNewObject, 1581 output, chassis, currentProfile); 1582 if (ret == CreatePIDRet::fail) 1583 { 1584 return; 1585 } 1586 if (ret == CreatePIDRet::del) 1587 { 1588 continue; 1589 } 1590 1591 if (!createNewObject) 1592 { 1593 for (const auto& property : output) 1594 { 1595 sdbusplus::asio::setProperty( 1596 *crow::connections::systemBus, 1597 "xyz.openbmc_project.EntityManager", path, iface, 1598 property.first, property.second, 1599 [response, 1600 propertyName{std::string(property.first)}]( 1601 const boost::system::error_code& ec) { 1602 if (ec) 1603 { 1604 BMCWEB_LOG_ERROR("Error patching {}: {}", 1605 propertyName, ec); 1606 messages::internalError(response->res); 1607 return; 1608 } 1609 messages::success(response->res); 1610 }); 1611 } 1612 } 1613 else 1614 { 1615 if (chassis.empty()) 1616 { 1617 BMCWEB_LOG_ERROR("Failed to get chassis from config"); 1618 messages::internalError(response->res); 1619 return; 1620 } 1621 1622 bool foundChassis = false; 1623 for (const auto& obj : managedObj) 1624 { 1625 if (boost::algorithm::ends_with(obj.first.str, chassis)) 1626 { 1627 chassis = obj.first.str; 1628 foundChassis = true; 1629 break; 1630 } 1631 } 1632 if (!foundChassis) 1633 { 1634 BMCWEB_LOG_ERROR("Failed to find chassis on dbus"); 1635 messages::resourceMissingAtURI( 1636 response->res, 1637 boost::urls::format("/redfish/v1/Chassis/{}", 1638 chassis)); 1639 return; 1640 } 1641 1642 crow::connections::systemBus->async_method_call( 1643 [response](const boost::system::error_code& ec) { 1644 if (ec) 1645 { 1646 BMCWEB_LOG_ERROR("Error Adding Pid Object {}", ec); 1647 messages::internalError(response->res); 1648 return; 1649 } 1650 messages::success(response->res); 1651 }, 1652 "xyz.openbmc_project.EntityManager", chassis, 1653 "xyz.openbmc_project.AddObject", "AddObject", output); 1654 } 1655 } 1656 } 1657 } 1658 1659 ~SetPIDValues() 1660 { 1661 try 1662 { 1663 pidSetDone(); 1664 } 1665 catch (...) 1666 { 1667 BMCWEB_LOG_CRITICAL("pidSetDone threw exception"); 1668 } 1669 } 1670 1671 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1672 std::vector<std::pair<std::string, std::optional<nlohmann::json>>> 1673 configuration; 1674 std::optional<std::string> profile; 1675 dbus::utility::ManagedObjectType managedObj; 1676 std::vector<std::string> supportedProfiles; 1677 std::string currentProfile; 1678 std::string profileConnection; 1679 std::string profilePath; 1680 size_t objectCount = 0; 1681 }; 1682 1683 /** 1684 * @brief Retrieves BMC manager location data over DBus 1685 * 1686 * @param[in] asyncResp Shared pointer for completing asynchronous calls 1687 * @param[in] connectionName - service name 1688 * @param[in] path - object path 1689 * @return none 1690 */ 1691 inline void getLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1692 const std::string& connectionName, 1693 const std::string& path) 1694 { 1695 BMCWEB_LOG_DEBUG("Get BMC manager Location data."); 1696 1697 sdbusplus::asio::getProperty<std::string>( 1698 *crow::connections::systemBus, connectionName, path, 1699 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 1700 [asyncResp](const boost::system::error_code& ec, 1701 const std::string& property) { 1702 if (ec) 1703 { 1704 BMCWEB_LOG_DEBUG("DBUS response error for " 1705 "Location"); 1706 messages::internalError(asyncResp->res); 1707 return; 1708 } 1709 1710 asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 1711 property; 1712 }); 1713 } 1714 // avoid name collision systems.hpp 1715 inline void 1716 managerGetLastResetTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1717 { 1718 BMCWEB_LOG_DEBUG("Getting Manager Last Reset Time"); 1719 1720 sdbusplus::asio::getProperty<uint64_t>( 1721 *crow::connections::systemBus, "xyz.openbmc_project.State.BMC", 1722 "/xyz/openbmc_project/state/bmc0", "xyz.openbmc_project.State.BMC", 1723 "LastRebootTime", 1724 [asyncResp](const boost::system::error_code& ec, 1725 const uint64_t lastResetTime) { 1726 if (ec) 1727 { 1728 BMCWEB_LOG_DEBUG("D-BUS response error {}", ec); 1729 return; 1730 } 1731 1732 // LastRebootTime is epoch time, in milliseconds 1733 // https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19 1734 uint64_t lastResetTimeStamp = lastResetTime / 1000; 1735 1736 // Convert to ISO 8601 standard 1737 asyncResp->res.jsonValue["LastResetTime"] = 1738 redfish::time_utils::getDateTimeUint(lastResetTimeStamp); 1739 }); 1740 } 1741 1742 /** 1743 * @brief Set the running firmware image 1744 * 1745 * @param[i,o] asyncResp - Async response object 1746 * @param[i] runningFirmwareTarget - Image to make the running image 1747 * 1748 * @return void 1749 */ 1750 inline void 1751 setActiveFirmwareImage(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1752 const std::string& runningFirmwareTarget) 1753 { 1754 // Get the Id from /redfish/v1/UpdateService/FirmwareInventory/<Id> 1755 std::string::size_type idPos = runningFirmwareTarget.rfind('/'); 1756 if (idPos == std::string::npos) 1757 { 1758 messages::propertyValueNotInList(asyncResp->res, runningFirmwareTarget, 1759 "@odata.id"); 1760 BMCWEB_LOG_DEBUG("Can't parse firmware ID!"); 1761 return; 1762 } 1763 idPos++; 1764 if (idPos >= runningFirmwareTarget.size()) 1765 { 1766 messages::propertyValueNotInList(asyncResp->res, runningFirmwareTarget, 1767 "@odata.id"); 1768 BMCWEB_LOG_DEBUG("Invalid firmware ID."); 1769 return; 1770 } 1771 std::string firmwareId = runningFirmwareTarget.substr(idPos); 1772 1773 // Make sure the image is valid before setting priority 1774 sdbusplus::message::object_path objPath("/xyz/openbmc_project/software"); 1775 dbus::utility::getManagedObjects( 1776 "xyz.openbmc_project.Software.BMC.Updater", objPath, 1777 [asyncResp, firmwareId, runningFirmwareTarget]( 1778 const boost::system::error_code& ec, 1779 const dbus::utility::ManagedObjectType& subtree) { 1780 if (ec) 1781 { 1782 BMCWEB_LOG_DEBUG("D-Bus response error getting objects."); 1783 messages::internalError(asyncResp->res); 1784 return; 1785 } 1786 1787 if (subtree.empty()) 1788 { 1789 BMCWEB_LOG_DEBUG("Can't find image!"); 1790 messages::internalError(asyncResp->res); 1791 return; 1792 } 1793 1794 bool foundImage = false; 1795 for (const auto& object : subtree) 1796 { 1797 const std::string& path = 1798 static_cast<const std::string&>(object.first); 1799 std::size_t idPos2 = path.rfind('/'); 1800 1801 if (idPos2 == std::string::npos) 1802 { 1803 continue; 1804 } 1805 1806 idPos2++; 1807 if (idPos2 >= path.size()) 1808 { 1809 continue; 1810 } 1811 1812 if (path.substr(idPos2) == firmwareId) 1813 { 1814 foundImage = true; 1815 break; 1816 } 1817 } 1818 1819 if (!foundImage) 1820 { 1821 messages::propertyValueNotInList( 1822 asyncResp->res, runningFirmwareTarget, "@odata.id"); 1823 BMCWEB_LOG_DEBUG("Invalid firmware ID."); 1824 return; 1825 } 1826 1827 BMCWEB_LOG_DEBUG("Setting firmware version {} to priority 0.", 1828 firmwareId); 1829 1830 // Only support Immediate 1831 // An addition could be a Redfish Setting like 1832 // ActiveSoftwareImageApplyTime and support OnReset 1833 sdbusplus::asio::setProperty( 1834 *crow::connections::systemBus, 1835 "xyz.openbmc_project.Software.BMC.Updater", 1836 "/xyz/openbmc_project/software/" + firmwareId, 1837 "xyz.openbmc_project.Software.RedundancyPriority", "Priority", 1838 static_cast<uint8_t>(0), 1839 [asyncResp](const boost::system::error_code& ec2) { 1840 if (ec2) 1841 { 1842 BMCWEB_LOG_DEBUG("D-Bus response error setting."); 1843 messages::internalError(asyncResp->res); 1844 return; 1845 } 1846 doBMCGracefulRestart(asyncResp); 1847 }); 1848 }); 1849 } 1850 1851 inline void setDateTime(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 1852 std::string datetime) 1853 { 1854 BMCWEB_LOG_DEBUG("Set date time: {}", datetime); 1855 1856 std::optional<redfish::time_utils::usSinceEpoch> us = 1857 redfish::time_utils::dateStringToEpoch(datetime); 1858 if (!us) 1859 { 1860 messages::propertyValueFormatError(asyncResp->res, datetime, 1861 "DateTime"); 1862 return; 1863 } 1864 sdbusplus::asio::setProperty( 1865 *crow::connections::systemBus, "xyz.openbmc_project.Time.Manager", 1866 "/xyz/openbmc_project/time/bmc", "xyz.openbmc_project.Time.EpochTime", 1867 "Elapsed", us->count(), 1868 [asyncResp{std::move(asyncResp)}, 1869 datetime{std::move(datetime)}](const boost::system::error_code& ec) { 1870 if (ec) 1871 { 1872 BMCWEB_LOG_DEBUG("Failed to set elapsed time. " 1873 "DBUS response error {}", 1874 ec); 1875 messages::internalError(asyncResp->res); 1876 return; 1877 } 1878 asyncResp->res.jsonValue["DateTime"] = datetime; 1879 }); 1880 } 1881 1882 inline void 1883 checkForQuiesced(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1884 { 1885 sdbusplus::asio::getProperty<std::string>( 1886 *crow::connections::systemBus, "org.freedesktop.systemd1", 1887 "/org/freedesktop/systemd1/unit/obmc-bmc-service-quiesce@0.target", 1888 "org.freedesktop.systemd1.Unit", "ActiveState", 1889 [asyncResp](const boost::system::error_code& ec, 1890 const std::string& val) { 1891 if (!ec) 1892 { 1893 if (val == "active") 1894 { 1895 asyncResp->res.jsonValue["Status"]["Health"] = "Critical"; 1896 asyncResp->res.jsonValue["Status"]["State"] = "Quiesced"; 1897 return; 1898 } 1899 } 1900 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 1901 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 1902 }); 1903 } 1904 1905 inline void requestRoutesManager(App& app) 1906 { 1907 std::string uuid = persistent_data::getConfig().systemUuid; 1908 1909 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/") 1910 .privileges(redfish::privileges::getManager) 1911 .methods(boost::beast::http::verb::get)( 1912 [&app, uuid](const crow::Request& req, 1913 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1914 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1915 { 1916 return; 1917 } 1918 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc"; 1919 asyncResp->res.jsonValue["@odata.type"] = "#Manager.v1_14_0.Manager"; 1920 asyncResp->res.jsonValue["Id"] = "bmc"; 1921 asyncResp->res.jsonValue["Name"] = "OpenBmc Manager"; 1922 asyncResp->res.jsonValue["Description"] = 1923 "Baseboard Management Controller"; 1924 asyncResp->res.jsonValue["PowerState"] = "On"; 1925 1926 asyncResp->res.jsonValue["ManagerType"] = "BMC"; 1927 asyncResp->res.jsonValue["UUID"] = systemd_utils::getUuid(); 1928 asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid; 1929 asyncResp->res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model 1930 1931 asyncResp->res.jsonValue["LogServices"]["@odata.id"] = 1932 "/redfish/v1/Managers/bmc/LogServices"; 1933 asyncResp->res.jsonValue["NetworkProtocol"]["@odata.id"] = 1934 "/redfish/v1/Managers/bmc/NetworkProtocol"; 1935 asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = 1936 "/redfish/v1/Managers/bmc/EthernetInterfaces"; 1937 1938 #ifdef BMCWEB_ENABLE_VM_NBDPROXY 1939 asyncResp->res.jsonValue["VirtualMedia"]["@odata.id"] = 1940 "/redfish/v1/Managers/bmc/VirtualMedia"; 1941 #endif // BMCWEB_ENABLE_VM_NBDPROXY 1942 1943 // default oem data 1944 nlohmann::json& oem = asyncResp->res.jsonValue["Oem"]; 1945 nlohmann::json& oemOpenbmc = oem["OpenBmc"]; 1946 oem["@odata.type"] = "#OemManager.Oem"; 1947 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem"; 1948 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc"; 1949 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc"; 1950 1951 nlohmann::json::object_t certificates; 1952 certificates["@odata.id"] = 1953 "/redfish/v1/Managers/bmc/Truststore/Certificates"; 1954 oemOpenbmc["Certificates"] = std::move(certificates); 1955 1956 // Manager.Reset (an action) can be many values, OpenBMC only 1957 // supports BMC reboot. 1958 nlohmann::json& managerReset = 1959 asyncResp->res.jsonValue["Actions"]["#Manager.Reset"]; 1960 managerReset["target"] = 1961 "/redfish/v1/Managers/bmc/Actions/Manager.Reset"; 1962 managerReset["@Redfish.ActionInfo"] = 1963 "/redfish/v1/Managers/bmc/ResetActionInfo"; 1964 1965 // ResetToDefaults (Factory Reset) has values like 1966 // PreserveNetworkAndUsers and PreserveNetwork that aren't supported 1967 // on OpenBMC 1968 nlohmann::json& resetToDefaults = 1969 asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"]; 1970 resetToDefaults["target"] = 1971 "/redfish/v1/Managers/bmc/Actions/Manager.ResetToDefaults"; 1972 resetToDefaults["ResetType@Redfish.AllowableValues"] = 1973 nlohmann::json::array_t({"ResetAll"}); 1974 1975 std::pair<std::string, std::string> redfishDateTimeOffset = 1976 redfish::time_utils::getDateTimeOffsetNow(); 1977 1978 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 1979 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 1980 redfishDateTimeOffset.second; 1981 1982 // TODO (Gunnar): Remove these one day since moved to ComputerSystem 1983 // Still used by OCP profiles 1984 // https://github.com/opencomputeproject/OCP-Profiles/issues/23 1985 // Fill in SerialConsole info 1986 asyncResp->res.jsonValue["SerialConsole"]["ServiceEnabled"] = true; 1987 asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15; 1988 asyncResp->res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = 1989 nlohmann::json::array_t({"IPMI", "SSH"}); 1990 #ifdef BMCWEB_ENABLE_KVM 1991 // Fill in GraphicalConsole info 1992 asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true; 1993 asyncResp->res.jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 1994 4; 1995 asyncResp->res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = 1996 nlohmann::json::array_t({"KVMIP"}); 1997 #endif // BMCWEB_ENABLE_KVM 1998 if constexpr (!bmcwebEnableMultiHost) 1999 { 2000 asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = 2001 1; 2002 2003 nlohmann::json::array_t managerForServers; 2004 nlohmann::json::object_t manager; 2005 manager["@odata.id"] = "/redfish/v1/Systems/system"; 2006 managerForServers.emplace_back(std::move(manager)); 2007 2008 asyncResp->res.jsonValue["Links"]["ManagerForServers"] = 2009 std::move(managerForServers); 2010 } 2011 if constexpr (bmcwebEnableHealthPopulate) 2012 { 2013 auto health = std::make_shared<HealthPopulate>(asyncResp); 2014 health->isManagersHealth = true; 2015 health->populate(); 2016 } 2017 2018 sw_util::populateSoftwareInformation(asyncResp, sw_util::bmcPurpose, 2019 "FirmwareVersion", true); 2020 2021 managerGetLastResetTime(asyncResp); 2022 2023 // ManagerDiagnosticData is added for all BMCs. 2024 nlohmann::json& managerDiagnosticData = 2025 asyncResp->res.jsonValue["ManagerDiagnosticData"]; 2026 managerDiagnosticData["@odata.id"] = 2027 "/redfish/v1/Managers/bmc/ManagerDiagnosticData"; 2028 2029 #ifdef BMCWEB_ENABLE_REDFISH_OEM_MANAGER_FAN_DATA 2030 auto pids = std::make_shared<GetPIDValues>(asyncResp); 2031 pids->run(); 2032 #endif 2033 2034 getMainChassisId(asyncResp, 2035 [](const std::string& chassisId, 2036 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { 2037 aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1; 2038 nlohmann::json::array_t managerForChassis; 2039 nlohmann::json::object_t managerObj; 2040 boost::urls::url chassiUrl = 2041 boost::urls::format("/redfish/v1/Chassis/{}", chassisId); 2042 managerObj["@odata.id"] = chassiUrl; 2043 managerForChassis.emplace_back(std::move(managerObj)); 2044 aRsp->res.jsonValue["Links"]["ManagerForChassis"] = 2045 std::move(managerForChassis); 2046 aRsp->res.jsonValue["Links"]["ManagerInChassis"]["@odata.id"] = 2047 chassiUrl; 2048 }); 2049 2050 sdbusplus::asio::getProperty<double>( 2051 *crow::connections::systemBus, "org.freedesktop.systemd1", 2052 "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", 2053 "Progress", 2054 [asyncResp](const boost::system::error_code& ec, double val) { 2055 if (ec) 2056 { 2057 BMCWEB_LOG_ERROR("Error while getting progress"); 2058 messages::internalError(asyncResp->res); 2059 return; 2060 } 2061 if (val < 1.0) 2062 { 2063 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 2064 asyncResp->res.jsonValue["Status"]["State"] = "Starting"; 2065 return; 2066 } 2067 checkForQuiesced(asyncResp); 2068 }); 2069 2070 constexpr std::array<std::string_view, 1> interfaces = { 2071 "xyz.openbmc_project.Inventory.Item.Bmc"}; 2072 dbus::utility::getSubTree( 2073 "/xyz/openbmc_project/inventory", 0, interfaces, 2074 [asyncResp]( 2075 const boost::system::error_code& ec, 2076 const dbus::utility::MapperGetSubTreeResponse& subtree) { 2077 if (ec) 2078 { 2079 BMCWEB_LOG_DEBUG("D-Bus response error on GetSubTree {}", ec); 2080 return; 2081 } 2082 if (subtree.empty()) 2083 { 2084 BMCWEB_LOG_DEBUG("Can't find bmc D-Bus object!"); 2085 return; 2086 } 2087 // Assume only 1 bmc D-Bus object 2088 // Throw an error if there is more than 1 2089 if (subtree.size() > 1) 2090 { 2091 BMCWEB_LOG_DEBUG("Found more than 1 bmc D-Bus object!"); 2092 messages::internalError(asyncResp->res); 2093 return; 2094 } 2095 2096 if (subtree[0].first.empty() || subtree[0].second.size() != 1) 2097 { 2098 BMCWEB_LOG_DEBUG("Error getting bmc D-Bus object!"); 2099 messages::internalError(asyncResp->res); 2100 return; 2101 } 2102 2103 const std::string& path = subtree[0].first; 2104 const std::string& connectionName = subtree[0].second[0].first; 2105 2106 for (const auto& interfaceName : subtree[0].second[0].second) 2107 { 2108 if (interfaceName == 2109 "xyz.openbmc_project.Inventory.Decorator.Asset") 2110 { 2111 sdbusplus::asio::getAllProperties( 2112 *crow::connections::systemBus, connectionName, path, 2113 "xyz.openbmc_project.Inventory.Decorator.Asset", 2114 [asyncResp](const boost::system::error_code& ec2, 2115 const dbus::utility::DBusPropertiesMap& 2116 propertiesList) { 2117 if (ec2) 2118 { 2119 BMCWEB_LOG_DEBUG("Can't get bmc asset!"); 2120 return; 2121 } 2122 2123 const std::string* partNumber = nullptr; 2124 const std::string* serialNumber = nullptr; 2125 const std::string* manufacturer = nullptr; 2126 const std::string* model = nullptr; 2127 const std::string* sparePartNumber = nullptr; 2128 2129 const bool success = sdbusplus::unpackPropertiesNoThrow( 2130 dbus_utils::UnpackErrorPrinter(), propertiesList, 2131 "PartNumber", partNumber, "SerialNumber", 2132 serialNumber, "Manufacturer", manufacturer, "Model", 2133 model, "SparePartNumber", sparePartNumber); 2134 2135 if (!success) 2136 { 2137 messages::internalError(asyncResp->res); 2138 return; 2139 } 2140 2141 if (partNumber != nullptr) 2142 { 2143 asyncResp->res.jsonValue["PartNumber"] = 2144 *partNumber; 2145 } 2146 2147 if (serialNumber != nullptr) 2148 { 2149 asyncResp->res.jsonValue["SerialNumber"] = 2150 *serialNumber; 2151 } 2152 2153 if (manufacturer != nullptr) 2154 { 2155 asyncResp->res.jsonValue["Manufacturer"] = 2156 *manufacturer; 2157 } 2158 2159 if (model != nullptr) 2160 { 2161 asyncResp->res.jsonValue["Model"] = *model; 2162 } 2163 2164 if (sparePartNumber != nullptr) 2165 { 2166 asyncResp->res.jsonValue["SparePartNumber"] = 2167 *sparePartNumber; 2168 } 2169 }); 2170 } 2171 else if (interfaceName == 2172 "xyz.openbmc_project.Inventory.Decorator.LocationCode") 2173 { 2174 getLocation(asyncResp, connectionName, path); 2175 } 2176 } 2177 }); 2178 }); 2179 2180 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/") 2181 .privileges(redfish::privileges::patchManager) 2182 .methods(boost::beast::http::verb::patch)( 2183 [&app](const crow::Request& req, 2184 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2185 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2186 { 2187 return; 2188 } 2189 std::optional<nlohmann::json> oem; 2190 std::optional<nlohmann::json> links; 2191 std::optional<std::string> datetime; 2192 2193 if (!json_util::readJsonPatch(req, asyncResp->res, "Oem", oem, 2194 "DateTime", datetime, "Links", links)) 2195 { 2196 return; 2197 } 2198 2199 if (oem) 2200 { 2201 #ifdef BMCWEB_ENABLE_REDFISH_OEM_MANAGER_FAN_DATA 2202 std::optional<nlohmann::json> openbmc; 2203 if (!redfish::json_util::readJson(*oem, asyncResp->res, "OpenBmc", 2204 openbmc)) 2205 { 2206 return; 2207 } 2208 if (openbmc) 2209 { 2210 std::optional<nlohmann::json> fan; 2211 if (!redfish::json_util::readJson(*openbmc, asyncResp->res, 2212 "Fan", fan)) 2213 { 2214 return; 2215 } 2216 if (fan) 2217 { 2218 auto pid = std::make_shared<SetPIDValues>(asyncResp, *fan); 2219 pid->run(); 2220 } 2221 } 2222 #else 2223 messages::propertyUnknown(asyncResp->res, "Oem"); 2224 return; 2225 #endif 2226 } 2227 if (links) 2228 { 2229 std::optional<nlohmann::json> activeSoftwareImage; 2230 if (!redfish::json_util::readJson(*links, asyncResp->res, 2231 "ActiveSoftwareImage", 2232 activeSoftwareImage)) 2233 { 2234 return; 2235 } 2236 if (activeSoftwareImage) 2237 { 2238 std::optional<std::string> odataId; 2239 if (!json_util::readJson(*activeSoftwareImage, asyncResp->res, 2240 "@odata.id", odataId)) 2241 { 2242 return; 2243 } 2244 2245 if (odataId) 2246 { 2247 setActiveFirmwareImage(asyncResp, *odataId); 2248 } 2249 } 2250 } 2251 if (datetime) 2252 { 2253 setDateTime(asyncResp, std::move(*datetime)); 2254 } 2255 }); 2256 } 2257 2258 inline void requestRoutesManagerCollection(App& app) 2259 { 2260 BMCWEB_ROUTE(app, "/redfish/v1/Managers/") 2261 .privileges(redfish::privileges::getManagerCollection) 2262 .methods(boost::beast::http::verb::get)( 2263 [&app](const crow::Request& req, 2264 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2265 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2266 { 2267 return; 2268 } 2269 // Collections don't include the static data added by SubRoute 2270 // because it has a duplicate entry for members 2271 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers"; 2272 asyncResp->res.jsonValue["@odata.type"] = 2273 "#ManagerCollection.ManagerCollection"; 2274 asyncResp->res.jsonValue["Name"] = "Manager Collection"; 2275 asyncResp->res.jsonValue["Members@odata.count"] = 1; 2276 nlohmann::json::array_t members; 2277 nlohmann::json& bmc = members.emplace_back(); 2278 bmc["@odata.id"] = "/redfish/v1/Managers/bmc"; 2279 asyncResp->res.jsonValue["Members"] = std::move(members); 2280 }); 2281 } 2282 } // namespace redfish 2283