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: " 151 << 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_ERROR << "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 << name << " not supported in current profile"; 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 << "\n"; 741 742 std::string escaped = value; 743 std::replace(escaped.begin(), escaped.end(), ' ', '_'); 744 escaped = "/" + escaped; 745 auto it = std::find_if(managedObj.begin(), managedObj.end(), 746 [&escaped](const auto& obj) { 747 if (boost::algorithm::ends_with(obj.first.str, escaped)) 748 { 749 BMCWEB_LOG_DEBUG << "Matched " << obj.first.str << "\n"; 750 return true; 751 } 752 return false; 753 }); 754 755 if (it == managedObj.end()) 756 { 757 return nullptr; 758 } 759 // 5 comes from <chassis-name> being the 5th element 760 // /xyz/openbmc_project/inventory/system/chassis/<chassis-name> 761 if (dbus::utility::getNthStringFromPath(it->first.str, 5, chassis)) 762 { 763 return &(*it); 764 } 765 766 return nullptr; 767 } 768 769 inline CreatePIDRet createPidInterface( 770 const std::shared_ptr<bmcweb::AsyncResp>& response, const std::string& type, 771 const nlohmann::json::iterator& it, const std::string& path, 772 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject, 773 dbus::utility::DBusPropertiesMap& output, std::string& chassis, 774 const std::string& profile) 775 { 776 // common deleter 777 if (it.value() == nullptr) 778 { 779 std::string iface; 780 if (type == "PidControllers" || type == "FanControllers") 781 { 782 iface = pidConfigurationIface; 783 } 784 else if (type == "FanZones") 785 { 786 iface = pidZoneConfigurationIface; 787 } 788 else if (type == "StepwiseControllers") 789 { 790 iface = stepwiseConfigurationIface; 791 } 792 else 793 { 794 BMCWEB_LOG_ERROR << "Illegal Type " << type; 795 messages::propertyUnknown(response->res, type); 796 return CreatePIDRet::fail; 797 } 798 799 BMCWEB_LOG_DEBUG << "del " << path << " " << iface << "\n"; 800 // delete interface 801 crow::connections::systemBus->async_method_call( 802 [response, path](const boost::system::error_code& ec) { 803 if (ec) 804 { 805 BMCWEB_LOG_ERROR << "Error patching " << path << ": " << ec; 806 messages::internalError(response->res); 807 return; 808 } 809 messages::success(response->res); 810 }, 811 "xyz.openbmc_project.EntityManager", path, iface, "Delete"); 812 return CreatePIDRet::del; 813 } 814 815 const dbus::utility::ManagedObjectType::value_type* managedItem = nullptr; 816 if (!createNewObject) 817 { 818 // if we aren't creating a new object, we should be able to find it on 819 // d-bus 820 managedItem = findChassis(managedObj, it.key(), chassis); 821 if (managedItem == nullptr) 822 { 823 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch"; 824 messages::invalidObject( 825 response->res, 826 boost::urls::format("/redfish/v1/Chassis/{}", chassis)); 827 return CreatePIDRet::fail; 828 } 829 } 830 831 if (!profile.empty() && 832 (type == "PidControllers" || type == "FanControllers" || 833 type == "StepwiseControllers")) 834 { 835 if (managedItem == nullptr) 836 { 837 output.emplace_back("Profiles", std::vector<std::string>{profile}); 838 } 839 else 840 { 841 std::string interface; 842 if (type == "StepwiseControllers") 843 { 844 interface = stepwiseConfigurationIface; 845 } 846 else 847 { 848 interface = pidConfigurationIface; 849 } 850 bool ifaceFound = false; 851 for (const auto& iface : managedItem->second) 852 { 853 if (iface.first == interface) 854 { 855 ifaceFound = true; 856 for (const auto& prop : iface.second) 857 { 858 if (prop.first == "Profiles") 859 { 860 const std::vector<std::string>* curProfiles = 861 std::get_if<std::vector<std::string>>( 862 &(prop.second)); 863 if (curProfiles == nullptr) 864 { 865 BMCWEB_LOG_ERROR 866 << "Illegal profiles in managed object"; 867 messages::internalError(response->res); 868 return CreatePIDRet::fail; 869 } 870 if (std::find(curProfiles->begin(), 871 curProfiles->end(), 872 profile) == curProfiles->end()) 873 { 874 std::vector<std::string> newProfiles = 875 *curProfiles; 876 newProfiles.push_back(profile); 877 output.emplace_back("Profiles", newProfiles); 878 } 879 } 880 } 881 } 882 } 883 884 if (!ifaceFound) 885 { 886 BMCWEB_LOG_ERROR 887 << "Failed to find interface in managed object"; 888 messages::internalError(response->res); 889 return CreatePIDRet::fail; 890 } 891 } 892 } 893 894 if (type == "PidControllers" || type == "FanControllers") 895 { 896 if (createNewObject) 897 { 898 output.emplace_back("Class", 899 type == "PidControllers" ? "temp" : "fan"); 900 output.emplace_back("Type", "Pid"); 901 } 902 903 std::optional<std::vector<nlohmann::json>> zones; 904 std::optional<std::vector<std::string>> inputs; 905 std::optional<std::vector<std::string>> outputs; 906 std::map<std::string, std::optional<double>> doubles; 907 std::optional<std::string> setpointOffset; 908 if (!redfish::json_util::readJson( 909 it.value(), response->res, "Inputs", inputs, "Outputs", outputs, 910 "Zones", zones, "FFGainCoefficient", 911 doubles["FFGainCoefficient"], "FFOffCoefficient", 912 doubles["FFOffCoefficient"], "ICoefficient", 913 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"], 914 "ILimitMin", doubles["ILimitMin"], "OutLimitMax", 915 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"], 916 "PCoefficient", doubles["PCoefficient"], "SetPoint", 917 doubles["SetPoint"], "SetPointOffset", setpointOffset, 918 "SlewNeg", doubles["SlewNeg"], "SlewPos", doubles["SlewPos"], 919 "PositiveHysteresis", doubles["PositiveHysteresis"], 920 "NegativeHysteresis", doubles["NegativeHysteresis"])) 921 { 922 return CreatePIDRet::fail; 923 } 924 if (zones) 925 { 926 std::vector<std::string> zonesStr; 927 if (!getZonesFromJsonReq(response, *zones, zonesStr)) 928 { 929 BMCWEB_LOG_ERROR << "Illegal Zones"; 930 return CreatePIDRet::fail; 931 } 932 if (chassis.empty() && 933 findChassis(managedObj, zonesStr[0], chassis) == nullptr) 934 { 935 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch"; 936 messages::invalidObject( 937 response->res, 938 boost::urls::format("/redfish/v1/Chassis/{}", chassis)); 939 return CreatePIDRet::fail; 940 } 941 output.emplace_back("Zones", std::move(zonesStr)); 942 } 943 944 if (inputs) 945 { 946 for (std::string& value : *inputs) 947 { 948 std::replace(value.begin(), value.end(), '_', ' '); 949 } 950 output.emplace_back("Inputs", *inputs); 951 } 952 953 if (outputs) 954 { 955 for (std::string& value : *outputs) 956 { 957 std::replace(value.begin(), value.end(), '_', ' '); 958 } 959 output.emplace_back("Outputs", *outputs); 960 } 961 962 if (setpointOffset) 963 { 964 // translate between redfish and dbus names 965 if (*setpointOffset == "UpperThresholdNonCritical") 966 { 967 output.emplace_back("SetPointOffset", "WarningLow"); 968 } 969 else if (*setpointOffset == "LowerThresholdNonCritical") 970 { 971 output.emplace_back("SetPointOffset", "WarningHigh"); 972 } 973 else if (*setpointOffset == "LowerThresholdCritical") 974 { 975 output.emplace_back("SetPointOffset", "CriticalLow"); 976 } 977 else if (*setpointOffset == "UpperThresholdCritical") 978 { 979 output.emplace_back("SetPointOffset", "CriticalHigh"); 980 } 981 else 982 { 983 BMCWEB_LOG_ERROR << "Invalid setpointoffset " 984 << *setpointOffset; 985 messages::propertyValueNotInList(response->res, it.key(), 986 "SetPointOffset"); 987 return CreatePIDRet::fail; 988 } 989 } 990 991 // doubles 992 for (const auto& pairs : doubles) 993 { 994 if (!pairs.second) 995 { 996 continue; 997 } 998 BMCWEB_LOG_DEBUG << pairs.first << " = " << *pairs.second; 999 output.emplace_back(pairs.first, *pairs.second); 1000 } 1001 } 1002 1003 else if (type == "FanZones") 1004 { 1005 output.emplace_back("Type", "Pid.Zone"); 1006 1007 std::optional<nlohmann::json> chassisContainer; 1008 std::optional<double> failSafePercent; 1009 std::optional<double> minThermalOutput; 1010 if (!redfish::json_util::readJson(it.value(), response->res, "Chassis", 1011 chassisContainer, "FailSafePercent", 1012 failSafePercent, "MinThermalOutput", 1013 minThermalOutput)) 1014 { 1015 return CreatePIDRet::fail; 1016 } 1017 1018 if (chassisContainer) 1019 { 1020 std::string chassisId; 1021 if (!redfish::json_util::readJson(*chassisContainer, response->res, 1022 "@odata.id", chassisId)) 1023 { 1024 return CreatePIDRet::fail; 1025 } 1026 1027 // /redfish/v1/chassis/chassis_name/ 1028 if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis)) 1029 { 1030 BMCWEB_LOG_ERROR << "Got invalid path " << chassisId; 1031 messages::invalidObject( 1032 response->res, 1033 boost::urls::format("/redfish/v1/Chassis/{}", chassisId)); 1034 return CreatePIDRet::fail; 1035 } 1036 } 1037 if (minThermalOutput) 1038 { 1039 output.emplace_back("MinThermalOutput", *minThermalOutput); 1040 } 1041 if (failSafePercent) 1042 { 1043 output.emplace_back("FailSafePercent", *failSafePercent); 1044 } 1045 } 1046 else if (type == "StepwiseControllers") 1047 { 1048 output.emplace_back("Type", "Stepwise"); 1049 1050 std::optional<std::vector<nlohmann::json>> zones; 1051 std::optional<std::vector<nlohmann::json>> steps; 1052 std::optional<std::vector<std::string>> inputs; 1053 std::optional<double> positiveHysteresis; 1054 std::optional<double> negativeHysteresis; 1055 std::optional<std::string> direction; // upper clipping curve vs lower 1056 if (!redfish::json_util::readJson( 1057 it.value(), response->res, "Zones", zones, "Steps", steps, 1058 "Inputs", inputs, "PositiveHysteresis", positiveHysteresis, 1059 "NegativeHysteresis", negativeHysteresis, "Direction", 1060 direction)) 1061 { 1062 return CreatePIDRet::fail; 1063 } 1064 1065 if (zones) 1066 { 1067 std::vector<std::string> zonesStrs; 1068 if (!getZonesFromJsonReq(response, *zones, zonesStrs)) 1069 { 1070 BMCWEB_LOG_ERROR << "Illegal Zones"; 1071 return CreatePIDRet::fail; 1072 } 1073 if (chassis.empty() && 1074 findChassis(managedObj, zonesStrs[0], chassis) == nullptr) 1075 { 1076 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch"; 1077 messages::invalidObject( 1078 response->res, 1079 boost::urls::format("/redfish/v1/Chassis/{}", chassis)); 1080 return CreatePIDRet::fail; 1081 } 1082 output.emplace_back("Zones", std::move(zonesStrs)); 1083 } 1084 if (steps) 1085 { 1086 std::vector<double> readings; 1087 std::vector<double> outputs; 1088 for (auto& step : *steps) 1089 { 1090 double target = 0.0; 1091 double out = 0.0; 1092 1093 if (!redfish::json_util::readJson(step, response->res, "Target", 1094 target, "Output", out)) 1095 { 1096 return CreatePIDRet::fail; 1097 } 1098 readings.emplace_back(target); 1099 outputs.emplace_back(out); 1100 } 1101 output.emplace_back("Reading", std::move(readings)); 1102 output.emplace_back("Output", std::move(outputs)); 1103 } 1104 if (inputs) 1105 { 1106 for (std::string& value : *inputs) 1107 { 1108 std::replace(value.begin(), value.end(), '_', ' '); 1109 } 1110 output.emplace_back("Inputs", std::move(*inputs)); 1111 } 1112 if (negativeHysteresis) 1113 { 1114 output.emplace_back("NegativeHysteresis", *negativeHysteresis); 1115 } 1116 if (positiveHysteresis) 1117 { 1118 output.emplace_back("PositiveHysteresis", *positiveHysteresis); 1119 } 1120 if (direction) 1121 { 1122 constexpr const std::array<const char*, 2> allowedDirections = { 1123 "Ceiling", "Floor"}; 1124 if (std::find(allowedDirections.begin(), allowedDirections.end(), 1125 *direction) == allowedDirections.end()) 1126 { 1127 messages::propertyValueTypeError(response->res, "Direction", 1128 *direction); 1129 return CreatePIDRet::fail; 1130 } 1131 output.emplace_back("Class", *direction); 1132 } 1133 } 1134 else 1135 { 1136 BMCWEB_LOG_ERROR << "Illegal Type " << type; 1137 messages::propertyUnknown(response->res, type); 1138 return CreatePIDRet::fail; 1139 } 1140 return CreatePIDRet::patch; 1141 } 1142 struct GetPIDValues : std::enable_shared_from_this<GetPIDValues> 1143 { 1144 struct CompletionValues 1145 { 1146 std::vector<std::string> supportedProfiles; 1147 std::string currentProfile; 1148 dbus::utility::MapperGetSubTreeResponse subtree; 1149 }; 1150 1151 explicit GetPIDValues( 1152 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) : 1153 asyncResp(asyncRespIn) 1154 1155 {} 1156 1157 void run() 1158 { 1159 std::shared_ptr<GetPIDValues> self = shared_from_this(); 1160 1161 // get all configurations 1162 constexpr std::array<std::string_view, 4> interfaces = { 1163 pidConfigurationIface, pidZoneConfigurationIface, 1164 objectManagerIface, stepwiseConfigurationIface}; 1165 dbus::utility::getSubTree( 1166 "/", 0, interfaces, 1167 [self]( 1168 const boost::system::error_code& ec, 1169 const dbus::utility::MapperGetSubTreeResponse& subtreeLocal) { 1170 if (ec) 1171 { 1172 BMCWEB_LOG_ERROR << ec; 1173 messages::internalError(self->asyncResp->res); 1174 return; 1175 } 1176 self->complete.subtree = subtreeLocal; 1177 }); 1178 1179 // at the same time get the selected profile 1180 constexpr std::array<std::string_view, 1> thermalModeIfaces = { 1181 thermalModeIface}; 1182 dbus::utility::getSubTree( 1183 "/", 0, thermalModeIfaces, 1184 [self]( 1185 const boost::system::error_code& ec, 1186 const dbus::utility::MapperGetSubTreeResponse& subtreeLocal) { 1187 if (ec || subtreeLocal.empty()) 1188 { 1189 return; 1190 } 1191 if (subtreeLocal[0].second.size() != 1) 1192 { 1193 // invalid mapper response, should never happen 1194 BMCWEB_LOG_ERROR << "GetPIDValues: Mapper Error"; 1195 messages::internalError(self->asyncResp->res); 1196 return; 1197 } 1198 1199 const std::string& path = subtreeLocal[0].first; 1200 const std::string& owner = subtreeLocal[0].second[0].first; 1201 1202 sdbusplus::asio::getAllProperties( 1203 *crow::connections::systemBus, owner, path, thermalModeIface, 1204 [path, owner, 1205 self](const boost::system::error_code& ec2, 1206 const dbus::utility::DBusPropertiesMap& resp) { 1207 if (ec2) 1208 { 1209 BMCWEB_LOG_ERROR 1210 << "GetPIDValues: Can't get thermalModeIface " << path; 1211 messages::internalError(self->asyncResp->res); 1212 return; 1213 } 1214 1215 const std::string* current = nullptr; 1216 const std::vector<std::string>* supported = nullptr; 1217 1218 const bool success = sdbusplus::unpackPropertiesNoThrow( 1219 dbus_utils::UnpackErrorPrinter(), resp, "Current", current, 1220 "Supported", supported); 1221 1222 if (!success) 1223 { 1224 messages::internalError(self->asyncResp->res); 1225 return; 1226 } 1227 1228 if (current == nullptr || supported == nullptr) 1229 { 1230 BMCWEB_LOG_ERROR 1231 << "GetPIDValues: thermal mode iface invalid " << path; 1232 messages::internalError(self->asyncResp->res); 1233 return; 1234 } 1235 self->complete.currentProfile = *current; 1236 self->complete.supportedProfiles = *supported; 1237 }); 1238 }); 1239 } 1240 1241 static void 1242 processingComplete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1243 const CompletionValues& completion) 1244 { 1245 if (asyncResp->res.result() != boost::beast::http::status::ok) 1246 { 1247 return; 1248 } 1249 // create map of <connection, path to objMgr>> 1250 boost::container::flat_map< 1251 std::string, std::string, std::less<>, 1252 std::vector<std::pair<std::string, std::string>>> 1253 objectMgrPaths; 1254 boost::container::flat_set<std::string, std::less<>, 1255 std::vector<std::string>> 1256 calledConnections; 1257 for (const auto& pathGroup : completion.subtree) 1258 { 1259 for (const auto& connectionGroup : pathGroup.second) 1260 { 1261 auto findConnection = 1262 calledConnections.find(connectionGroup.first); 1263 if (findConnection != calledConnections.end()) 1264 { 1265 break; 1266 } 1267 for (const std::string& interface : connectionGroup.second) 1268 { 1269 if (interface == objectManagerIface) 1270 { 1271 objectMgrPaths[connectionGroup.first] = pathGroup.first; 1272 } 1273 // this list is alphabetical, so we 1274 // should have found the objMgr by now 1275 if (interface == pidConfigurationIface || 1276 interface == pidZoneConfigurationIface || 1277 interface == stepwiseConfigurationIface) 1278 { 1279 auto findObjMgr = 1280 objectMgrPaths.find(connectionGroup.first); 1281 if (findObjMgr == objectMgrPaths.end()) 1282 { 1283 BMCWEB_LOG_DEBUG << connectionGroup.first 1284 << "Has no Object Manager"; 1285 continue; 1286 } 1287 1288 calledConnections.insert(connectionGroup.first); 1289 1290 asyncPopulatePid(findObjMgr->first, findObjMgr->second, 1291 completion.currentProfile, 1292 completion.supportedProfiles, 1293 asyncResp); 1294 break; 1295 } 1296 } 1297 } 1298 } 1299 } 1300 1301 ~GetPIDValues() 1302 { 1303 boost::asio::post(crow::connections::systemBus->get_io_context(), 1304 std::bind_front(&processingComplete, asyncResp, 1305 std::move(complete))); 1306 } 1307 1308 GetPIDValues(const GetPIDValues&) = delete; 1309 GetPIDValues(GetPIDValues&&) = delete; 1310 GetPIDValues& operator=(const GetPIDValues&) = delete; 1311 GetPIDValues& operator=(GetPIDValues&&) = delete; 1312 1313 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1314 CompletionValues complete; 1315 }; 1316 1317 struct SetPIDValues : std::enable_shared_from_this<SetPIDValues> 1318 { 1319 SetPIDValues(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn, 1320 nlohmann::json& data) : 1321 asyncResp(asyncRespIn) 1322 { 1323 std::optional<nlohmann::json> pidControllers; 1324 std::optional<nlohmann::json> fanControllers; 1325 std::optional<nlohmann::json> fanZones; 1326 std::optional<nlohmann::json> stepwiseControllers; 1327 1328 if (!redfish::json_util::readJson( 1329 data, asyncResp->res, "PidControllers", pidControllers, 1330 "FanControllers", fanControllers, "FanZones", fanZones, 1331 "StepwiseControllers", stepwiseControllers, "Profile", profile)) 1332 { 1333 return; 1334 } 1335 configuration.emplace_back("PidControllers", std::move(pidControllers)); 1336 configuration.emplace_back("FanControllers", std::move(fanControllers)); 1337 configuration.emplace_back("FanZones", std::move(fanZones)); 1338 configuration.emplace_back("StepwiseControllers", 1339 std::move(stepwiseControllers)); 1340 } 1341 1342 SetPIDValues(const SetPIDValues&) = delete; 1343 SetPIDValues(SetPIDValues&&) = delete; 1344 SetPIDValues& operator=(const SetPIDValues&) = delete; 1345 SetPIDValues& operator=(SetPIDValues&&) = delete; 1346 1347 void run() 1348 { 1349 if (asyncResp->res.result() != boost::beast::http::status::ok) 1350 { 1351 return; 1352 } 1353 1354 std::shared_ptr<SetPIDValues> self = shared_from_this(); 1355 1356 // todo(james): might make sense to do a mapper call here if this 1357 // interface gets more traction 1358 sdbusplus::message::object_path objPath( 1359 "/xyz/openbmc_project/inventory"); 1360 dbus::utility::getManagedObjects( 1361 "xyz.openbmc_project.EntityManager", objPath, 1362 [self](const boost::system::error_code& ec, 1363 const dbus::utility::ManagedObjectType& mObj) { 1364 if (ec) 1365 { 1366 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager"; 1367 messages::internalError(self->asyncResp->res); 1368 return; 1369 } 1370 const std::array<const char*, 3> configurations = { 1371 pidConfigurationIface, pidZoneConfigurationIface, 1372 stepwiseConfigurationIface}; 1373 1374 for (const auto& [path, object] : mObj) 1375 { 1376 for (const auto& [interface, _] : object) 1377 { 1378 if (std::find(configurations.begin(), configurations.end(), 1379 interface) != configurations.end()) 1380 { 1381 self->objectCount++; 1382 break; 1383 } 1384 } 1385 } 1386 self->managedObj = mObj; 1387 }); 1388 1389 // at the same time get the profile information 1390 constexpr std::array<std::string_view, 1> thermalModeIfaces = { 1391 thermalModeIface}; 1392 dbus::utility::getSubTree( 1393 "/", 0, thermalModeIfaces, 1394 [self](const boost::system::error_code& ec, 1395 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1396 if (ec || subtree.empty()) 1397 { 1398 return; 1399 } 1400 if (subtree[0].second.empty()) 1401 { 1402 // invalid mapper response, should never happen 1403 BMCWEB_LOG_ERROR << "SetPIDValues: Mapper Error"; 1404 messages::internalError(self->asyncResp->res); 1405 return; 1406 } 1407 1408 const std::string& path = subtree[0].first; 1409 const std::string& owner = subtree[0].second[0].first; 1410 sdbusplus::asio::getAllProperties( 1411 *crow::connections::systemBus, owner, path, thermalModeIface, 1412 [self, path, owner](const boost::system::error_code& ec2, 1413 const dbus::utility::DBusPropertiesMap& r) { 1414 if (ec2) 1415 { 1416 BMCWEB_LOG_ERROR 1417 << "SetPIDValues: Can't get thermalModeIface " << path; 1418 messages::internalError(self->asyncResp->res); 1419 return; 1420 } 1421 const std::string* current = nullptr; 1422 const std::vector<std::string>* supported = nullptr; 1423 1424 const bool success = sdbusplus::unpackPropertiesNoThrow( 1425 dbus_utils::UnpackErrorPrinter(), r, "Current", current, 1426 "Supported", supported); 1427 1428 if (!success) 1429 { 1430 messages::internalError(self->asyncResp->res); 1431 return; 1432 } 1433 1434 if (current == nullptr || supported == nullptr) 1435 { 1436 BMCWEB_LOG_ERROR 1437 << "SetPIDValues: thermal mode iface invalid " << path; 1438 messages::internalError(self->asyncResp->res); 1439 return; 1440 } 1441 self->currentProfile = *current; 1442 self->supportedProfiles = *supported; 1443 self->profileConnection = owner; 1444 self->profilePath = path; 1445 }); 1446 }); 1447 } 1448 void pidSetDone() 1449 { 1450 if (asyncResp->res.result() != boost::beast::http::status::ok) 1451 { 1452 return; 1453 } 1454 std::shared_ptr<bmcweb::AsyncResp> response = asyncResp; 1455 if (profile) 1456 { 1457 if (std::find(supportedProfiles.begin(), supportedProfiles.end(), 1458 *profile) == supportedProfiles.end()) 1459 { 1460 messages::actionParameterUnknown(response->res, "Profile", 1461 *profile); 1462 return; 1463 } 1464 currentProfile = *profile; 1465 sdbusplus::asio::setProperty( 1466 *crow::connections::systemBus, profileConnection, profilePath, 1467 thermalModeIface, "Current", *profile, 1468 [response](const boost::system::error_code& ec) { 1469 if (ec) 1470 { 1471 BMCWEB_LOG_ERROR << "Error patching profile" << ec; 1472 messages::internalError(response->res); 1473 } 1474 }); 1475 } 1476 1477 for (auto& containerPair : configuration) 1478 { 1479 auto& container = containerPair.second; 1480 if (!container) 1481 { 1482 continue; 1483 } 1484 BMCWEB_LOG_DEBUG << *container; 1485 1486 const std::string& type = containerPair.first; 1487 1488 for (nlohmann::json::iterator it = container->begin(); 1489 it != container->end(); ++it) 1490 { 1491 const auto& name = it.key(); 1492 std::string dbusObjName = name; 1493 std::replace(dbusObjName.begin(), dbusObjName.end(), ' ', '_'); 1494 BMCWEB_LOG_DEBUG << "looking for " << name; 1495 1496 auto pathItr = std::find_if(managedObj.begin(), 1497 managedObj.end(), 1498 [&dbusObjName](const auto& obj) { 1499 return boost::algorithm::ends_with(obj.first.str, 1500 "/" + dbusObjName); 1501 }); 1502 dbus::utility::DBusPropertiesMap output; 1503 1504 output.reserve(16); // The pid interface length 1505 1506 // determines if we're patching entity-manager or 1507 // creating a new object 1508 bool createNewObject = (pathItr == managedObj.end()); 1509 BMCWEB_LOG_DEBUG << "Found = " << !createNewObject; 1510 1511 std::string iface; 1512 if (!createNewObject) 1513 { 1514 bool findInterface = false; 1515 for (const auto& interface : pathItr->second) 1516 { 1517 if (interface.first == pidConfigurationIface) 1518 { 1519 if (type == "PidControllers" || 1520 type == "FanControllers") 1521 { 1522 iface = pidConfigurationIface; 1523 findInterface = true; 1524 break; 1525 } 1526 } 1527 else if (interface.first == pidZoneConfigurationIface) 1528 { 1529 if (type == "FanZones") 1530 { 1531 iface = pidConfigurationIface; 1532 findInterface = true; 1533 break; 1534 } 1535 } 1536 else if (interface.first == stepwiseConfigurationIface) 1537 { 1538 if (type == "StepwiseControllers") 1539 { 1540 iface = stepwiseConfigurationIface; 1541 findInterface = true; 1542 break; 1543 } 1544 } 1545 } 1546 1547 // create new object if interface not found 1548 if (!findInterface) 1549 { 1550 createNewObject = true; 1551 } 1552 } 1553 1554 if (createNewObject && it.value() == nullptr) 1555 { 1556 // can't delete a non-existent object 1557 messages::propertyValueNotInList(response->res, it.value(), 1558 name); 1559 continue; 1560 } 1561 1562 std::string path; 1563 if (pathItr != managedObj.end()) 1564 { 1565 path = pathItr->first.str; 1566 } 1567 1568 BMCWEB_LOG_DEBUG << "Create new = " << createNewObject << "\n"; 1569 1570 // arbitrary limit to avoid attacks 1571 constexpr const size_t controllerLimit = 500; 1572 if (createNewObject && objectCount >= controllerLimit) 1573 { 1574 messages::resourceExhaustion(response->res, type); 1575 continue; 1576 } 1577 std::string escaped = name; 1578 std::replace(escaped.begin(), escaped.end(), '_', ' '); 1579 output.emplace_back("Name", escaped); 1580 1581 std::string chassis; 1582 CreatePIDRet ret = createPidInterface( 1583 response, type, it, path, managedObj, createNewObject, 1584 output, chassis, currentProfile); 1585 if (ret == CreatePIDRet::fail) 1586 { 1587 return; 1588 } 1589 if (ret == CreatePIDRet::del) 1590 { 1591 continue; 1592 } 1593 1594 if (!createNewObject) 1595 { 1596 for (const auto& property : output) 1597 { 1598 sdbusplus::asio::setProperty( 1599 *crow::connections::systemBus, 1600 "xyz.openbmc_project.EntityManager", path, iface, 1601 property.first, property.second, 1602 [response, 1603 propertyName{std::string(property.first)}]( 1604 const boost::system::error_code& ec) { 1605 if (ec) 1606 { 1607 BMCWEB_LOG_ERROR << "Error patching " 1608 << propertyName << ": " << ec; 1609 messages::internalError(response->res); 1610 return; 1611 } 1612 messages::success(response->res); 1613 }); 1614 } 1615 } 1616 else 1617 { 1618 if (chassis.empty()) 1619 { 1620 BMCWEB_LOG_ERROR << "Failed to get chassis from config"; 1621 messages::internalError(response->res); 1622 return; 1623 } 1624 1625 bool foundChassis = false; 1626 for (const auto& obj : managedObj) 1627 { 1628 if (boost::algorithm::ends_with(obj.first.str, chassis)) 1629 { 1630 chassis = obj.first.str; 1631 foundChassis = true; 1632 break; 1633 } 1634 } 1635 if (!foundChassis) 1636 { 1637 BMCWEB_LOG_ERROR << "Failed to find chassis on dbus"; 1638 messages::resourceMissingAtURI( 1639 response->res, 1640 boost::urls::format("/redfish/v1/Chassis/{}", 1641 chassis)); 1642 return; 1643 } 1644 1645 crow::connections::systemBus->async_method_call( 1646 [response](const boost::system::error_code& ec) { 1647 if (ec) 1648 { 1649 BMCWEB_LOG_ERROR << "Error Adding Pid Object " 1650 << ec; 1651 messages::internalError(response->res); 1652 return; 1653 } 1654 messages::success(response->res); 1655 }, 1656 "xyz.openbmc_project.EntityManager", chassis, 1657 "xyz.openbmc_project.AddObject", "AddObject", output); 1658 } 1659 } 1660 } 1661 } 1662 1663 ~SetPIDValues() 1664 { 1665 try 1666 { 1667 pidSetDone(); 1668 } 1669 catch (...) 1670 { 1671 BMCWEB_LOG_CRITICAL << "pidSetDone threw exception"; 1672 } 1673 } 1674 1675 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1676 std::vector<std::pair<std::string, std::optional<nlohmann::json>>> 1677 configuration; 1678 std::optional<std::string> profile; 1679 dbus::utility::ManagedObjectType managedObj; 1680 std::vector<std::string> supportedProfiles; 1681 std::string currentProfile; 1682 std::string profileConnection; 1683 std::string profilePath; 1684 size_t objectCount = 0; 1685 }; 1686 1687 /** 1688 * @brief Retrieves BMC manager location data over DBus 1689 * 1690 * @param[in] asyncResp Shared pointer for completing asynchronous calls 1691 * @param[in] connectionName - service name 1692 * @param[in] path - object path 1693 * @return none 1694 */ 1695 inline void getLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1696 const std::string& connectionName, 1697 const std::string& path) 1698 { 1699 BMCWEB_LOG_DEBUG << "Get BMC manager Location data."; 1700 1701 sdbusplus::asio::getProperty<std::string>( 1702 *crow::connections::systemBus, connectionName, path, 1703 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 1704 [asyncResp](const boost::system::error_code& ec, 1705 const std::string& property) { 1706 if (ec) 1707 { 1708 BMCWEB_LOG_DEBUG << "DBUS response error for " 1709 "Location"; 1710 messages::internalError(asyncResp->res); 1711 return; 1712 } 1713 1714 asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 1715 property; 1716 }); 1717 } 1718 // avoid name collision systems.hpp 1719 inline void 1720 managerGetLastResetTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1721 { 1722 BMCWEB_LOG_DEBUG << "Getting Manager Last Reset Time"; 1723 1724 sdbusplus::asio::getProperty<uint64_t>( 1725 *crow::connections::systemBus, "xyz.openbmc_project.State.BMC", 1726 "/xyz/openbmc_project/state/bmc0", "xyz.openbmc_project.State.BMC", 1727 "LastRebootTime", 1728 [asyncResp](const boost::system::error_code& ec, 1729 const uint64_t lastResetTime) { 1730 if (ec) 1731 { 1732 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec; 1733 return; 1734 } 1735 1736 // LastRebootTime is epoch time, in milliseconds 1737 // https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19 1738 uint64_t lastResetTimeStamp = lastResetTime / 1000; 1739 1740 // Convert to ISO 8601 standard 1741 asyncResp->res.jsonValue["LastResetTime"] = 1742 redfish::time_utils::getDateTimeUint(lastResetTimeStamp); 1743 }); 1744 } 1745 1746 /** 1747 * @brief Set the running firmware image 1748 * 1749 * @param[i,o] asyncResp - Async response object 1750 * @param[i] runningFirmwareTarget - Image to make the running image 1751 * 1752 * @return void 1753 */ 1754 inline void 1755 setActiveFirmwareImage(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1756 const std::string& runningFirmwareTarget) 1757 { 1758 // Get the Id from /redfish/v1/UpdateService/FirmwareInventory/<Id> 1759 std::string::size_type idPos = runningFirmwareTarget.rfind('/'); 1760 if (idPos == std::string::npos) 1761 { 1762 messages::propertyValueNotInList(asyncResp->res, runningFirmwareTarget, 1763 "@odata.id"); 1764 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!"; 1765 return; 1766 } 1767 idPos++; 1768 if (idPos >= runningFirmwareTarget.size()) 1769 { 1770 messages::propertyValueNotInList(asyncResp->res, runningFirmwareTarget, 1771 "@odata.id"); 1772 BMCWEB_LOG_DEBUG << "Invalid firmware ID."; 1773 return; 1774 } 1775 std::string firmwareId = runningFirmwareTarget.substr(idPos); 1776 1777 // Make sure the image is valid before setting priority 1778 sdbusplus::message::object_path objPath("/xyz/openbmc_project/software"); 1779 dbus::utility::getManagedObjects( 1780 "xyz.openbmc_project.Software.BMC.Updater", objPath, 1781 [asyncResp, firmwareId, runningFirmwareTarget]( 1782 const boost::system::error_code& ec, 1783 const dbus::utility::ManagedObjectType& subtree) { 1784 if (ec) 1785 { 1786 BMCWEB_LOG_DEBUG << "D-Bus response error getting objects."; 1787 messages::internalError(asyncResp->res); 1788 return; 1789 } 1790 1791 if (subtree.empty()) 1792 { 1793 BMCWEB_LOG_DEBUG << "Can't find image!"; 1794 messages::internalError(asyncResp->res); 1795 return; 1796 } 1797 1798 bool foundImage = false; 1799 for (const auto& object : subtree) 1800 { 1801 const std::string& path = 1802 static_cast<const std::string&>(object.first); 1803 std::size_t idPos2 = path.rfind('/'); 1804 1805 if (idPos2 == std::string::npos) 1806 { 1807 continue; 1808 } 1809 1810 idPos2++; 1811 if (idPos2 >= path.size()) 1812 { 1813 continue; 1814 } 1815 1816 if (path.substr(idPos2) == firmwareId) 1817 { 1818 foundImage = true; 1819 break; 1820 } 1821 } 1822 1823 if (!foundImage) 1824 { 1825 messages::propertyValueNotInList( 1826 asyncResp->res, runningFirmwareTarget, "@odata.id"); 1827 BMCWEB_LOG_DEBUG << "Invalid firmware ID."; 1828 return; 1829 } 1830 1831 BMCWEB_LOG_DEBUG << "Setting firmware version " << firmwareId 1832 << " to priority 0."; 1833 1834 // Only support Immediate 1835 // An addition could be a Redfish Setting like 1836 // ActiveSoftwareImageApplyTime and support OnReset 1837 sdbusplus::asio::setProperty( 1838 *crow::connections::systemBus, 1839 "xyz.openbmc_project.Software.BMC.Updater", 1840 "/xyz/openbmc_project/software/" + firmwareId, 1841 "xyz.openbmc_project.Software.RedundancyPriority", "Priority", 1842 static_cast<uint8_t>(0), 1843 [asyncResp](const boost::system::error_code& ec2) { 1844 if (ec2) 1845 { 1846 BMCWEB_LOG_DEBUG << "D-Bus response error setting."; 1847 messages::internalError(asyncResp->res); 1848 return; 1849 } 1850 doBMCGracefulRestart(asyncResp); 1851 }); 1852 }); 1853 } 1854 1855 inline void setDateTime(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 1856 std::string datetime) 1857 { 1858 BMCWEB_LOG_DEBUG << "Set date time: " << datetime; 1859 1860 std::optional<redfish::time_utils::usSinceEpoch> us = 1861 redfish::time_utils::dateStringToEpoch(datetime); 1862 if (!us) 1863 { 1864 messages::propertyValueFormatError(asyncResp->res, datetime, 1865 "DateTime"); 1866 return; 1867 } 1868 sdbusplus::asio::setProperty( 1869 *crow::connections::systemBus, "xyz.openbmc_project.Time.Manager", 1870 "/xyz/openbmc_project/time/bmc", "xyz.openbmc_project.Time.EpochTime", 1871 "Elapsed", us->count(), 1872 [asyncResp{std::move(asyncResp)}, 1873 datetime{std::move(datetime)}](const boost::system::error_code& ec) { 1874 if (ec) 1875 { 1876 BMCWEB_LOG_DEBUG << "Failed to set elapsed time. " 1877 "DBUS response error " 1878 << ec; 1879 messages::internalError(asyncResp->res); 1880 return; 1881 } 1882 asyncResp->res.jsonValue["DateTime"] = datetime; 1883 }); 1884 } 1885 1886 inline void 1887 checkForQuiesced(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1888 { 1889 sdbusplus::asio::getProperty<std::string>( 1890 *crow::connections::systemBus, "org.freedesktop.systemd1", 1891 "/org/freedesktop/systemd1/unit/obmc-bmc-service-quiesce@0.target", 1892 "org.freedesktop.systemd1.Unit", "ActiveState", 1893 [asyncResp](const boost::system::error_code& ec, 1894 const std::string& val) { 1895 if (!ec) 1896 { 1897 if (val == "active") 1898 { 1899 asyncResp->res.jsonValue["Status"]["Health"] = "Critical"; 1900 asyncResp->res.jsonValue["Status"]["State"] = "Quiesced"; 1901 return; 1902 } 1903 } 1904 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 1905 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 1906 }); 1907 } 1908 1909 inline void requestRoutesManager(App& app) 1910 { 1911 std::string uuid = persistent_data::getConfig().systemUuid; 1912 1913 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/") 1914 .privileges(redfish::privileges::getManager) 1915 .methods(boost::beast::http::verb::get)( 1916 [&app, uuid](const crow::Request& req, 1917 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1918 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1919 { 1920 return; 1921 } 1922 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc"; 1923 asyncResp->res.jsonValue["@odata.type"] = "#Manager.v1_14_0.Manager"; 1924 asyncResp->res.jsonValue["Id"] = "bmc"; 1925 asyncResp->res.jsonValue["Name"] = "OpenBmc Manager"; 1926 asyncResp->res.jsonValue["Description"] = 1927 "Baseboard Management Controller"; 1928 asyncResp->res.jsonValue["PowerState"] = "On"; 1929 1930 asyncResp->res.jsonValue["ManagerType"] = "BMC"; 1931 asyncResp->res.jsonValue["UUID"] = systemd_utils::getUuid(); 1932 asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid; 1933 asyncResp->res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model 1934 1935 asyncResp->res.jsonValue["LogServices"]["@odata.id"] = 1936 "/redfish/v1/Managers/bmc/LogServices"; 1937 asyncResp->res.jsonValue["NetworkProtocol"]["@odata.id"] = 1938 "/redfish/v1/Managers/bmc/NetworkProtocol"; 1939 asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = 1940 "/redfish/v1/Managers/bmc/EthernetInterfaces"; 1941 1942 #ifdef BMCWEB_ENABLE_VM_NBDPROXY 1943 asyncResp->res.jsonValue["VirtualMedia"]["@odata.id"] = 1944 "/redfish/v1/Managers/bmc/VirtualMedia"; 1945 #endif // BMCWEB_ENABLE_VM_NBDPROXY 1946 1947 // default oem data 1948 nlohmann::json& oem = asyncResp->res.jsonValue["Oem"]; 1949 nlohmann::json& oemOpenbmc = oem["OpenBmc"]; 1950 oem["@odata.type"] = "#OemManager.Oem"; 1951 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem"; 1952 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc"; 1953 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc"; 1954 1955 nlohmann::json::object_t certificates; 1956 certificates["@odata.id"] = 1957 "/redfish/v1/Managers/bmc/Truststore/Certificates"; 1958 oemOpenbmc["Certificates"] = std::move(certificates); 1959 1960 // Manager.Reset (an action) can be many values, OpenBMC only 1961 // supports BMC reboot. 1962 nlohmann::json& managerReset = 1963 asyncResp->res.jsonValue["Actions"]["#Manager.Reset"]; 1964 managerReset["target"] = 1965 "/redfish/v1/Managers/bmc/Actions/Manager.Reset"; 1966 managerReset["@Redfish.ActionInfo"] = 1967 "/redfish/v1/Managers/bmc/ResetActionInfo"; 1968 1969 // ResetToDefaults (Factory Reset) has values like 1970 // PreserveNetworkAndUsers and PreserveNetwork that aren't supported 1971 // on OpenBMC 1972 nlohmann::json& resetToDefaults = 1973 asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"]; 1974 resetToDefaults["target"] = 1975 "/redfish/v1/Managers/bmc/Actions/Manager.ResetToDefaults"; 1976 resetToDefaults["ResetType@Redfish.AllowableValues"] = 1977 nlohmann::json::array_t({"ResetAll"}); 1978 1979 std::pair<std::string, std::string> redfishDateTimeOffset = 1980 redfish::time_utils::getDateTimeOffsetNow(); 1981 1982 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 1983 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 1984 redfishDateTimeOffset.second; 1985 1986 // TODO (Gunnar): Remove these one day since moved to ComputerSystem 1987 // Still used by OCP profiles 1988 // https://github.com/opencomputeproject/OCP-Profiles/issues/23 1989 // Fill in SerialConsole info 1990 asyncResp->res.jsonValue["SerialConsole"]["ServiceEnabled"] = true; 1991 asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15; 1992 asyncResp->res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = 1993 nlohmann::json::array_t({"IPMI", "SSH"}); 1994 #ifdef BMCWEB_ENABLE_KVM 1995 // Fill in GraphicalConsole info 1996 asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true; 1997 asyncResp->res.jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 1998 4; 1999 asyncResp->res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = 2000 nlohmann::json::array_t({"KVMIP"}); 2001 #endif // BMCWEB_ENABLE_KVM 2002 if constexpr (!bmcwebEnableMultiHost) 2003 { 2004 asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = 2005 1; 2006 2007 nlohmann::json::array_t managerForServers; 2008 nlohmann::json::object_t manager; 2009 manager["@odata.id"] = "/redfish/v1/Systems/system"; 2010 managerForServers.emplace_back(std::move(manager)); 2011 2012 asyncResp->res.jsonValue["Links"]["ManagerForServers"] = 2013 std::move(managerForServers); 2014 } 2015 if constexpr (bmcwebEnableHealthPopulate) 2016 { 2017 auto health = std::make_shared<HealthPopulate>(asyncResp); 2018 health->isManagersHealth = true; 2019 health->populate(); 2020 } 2021 2022 sw_util::populateSoftwareInformation(asyncResp, sw_util::bmcPurpose, 2023 "FirmwareVersion", true); 2024 2025 managerGetLastResetTime(asyncResp); 2026 2027 // ManagerDiagnosticData is added for all BMCs. 2028 nlohmann::json& managerDiagnosticData = 2029 asyncResp->res.jsonValue["ManagerDiagnosticData"]; 2030 managerDiagnosticData["@odata.id"] = 2031 "/redfish/v1/Managers/bmc/ManagerDiagnosticData"; 2032 2033 #ifdef BMCWEB_ENABLE_REDFISH_OEM_MANAGER_FAN_DATA 2034 auto pids = std::make_shared<GetPIDValues>(asyncResp); 2035 pids->run(); 2036 #endif 2037 2038 getMainChassisId(asyncResp, 2039 [](const std::string& chassisId, 2040 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { 2041 aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1; 2042 nlohmann::json::array_t managerForChassis; 2043 nlohmann::json::object_t managerObj; 2044 boost::urls::url chassiUrl = 2045 boost::urls::format("/redfish/v1/Chassis/{}", chassisId); 2046 managerObj["@odata.id"] = chassiUrl; 2047 managerForChassis.emplace_back(std::move(managerObj)); 2048 aRsp->res.jsonValue["Links"]["ManagerForChassis"] = 2049 std::move(managerForChassis); 2050 aRsp->res.jsonValue["Links"]["ManagerInChassis"]["@odata.id"] = 2051 chassiUrl; 2052 }); 2053 2054 sdbusplus::asio::getProperty<double>( 2055 *crow::connections::systemBus, "org.freedesktop.systemd1", 2056 "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", 2057 "Progress", 2058 [asyncResp](const boost::system::error_code& ec, double val) { 2059 if (ec) 2060 { 2061 BMCWEB_LOG_ERROR << "Error while getting progress"; 2062 messages::internalError(asyncResp->res); 2063 return; 2064 } 2065 if (val < 1.0) 2066 { 2067 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 2068 asyncResp->res.jsonValue["Status"]["State"] = "Starting"; 2069 return; 2070 } 2071 checkForQuiesced(asyncResp); 2072 }); 2073 2074 constexpr std::array<std::string_view, 1> interfaces = { 2075 "xyz.openbmc_project.Inventory.Item.Bmc"}; 2076 dbus::utility::getSubTree( 2077 "/xyz/openbmc_project/inventory", 0, interfaces, 2078 [asyncResp]( 2079 const boost::system::error_code& ec, 2080 const dbus::utility::MapperGetSubTreeResponse& subtree) { 2081 if (ec) 2082 { 2083 BMCWEB_LOG_DEBUG << "D-Bus response error on GetSubTree " << ec; 2084 return; 2085 } 2086 if (subtree.empty()) 2087 { 2088 BMCWEB_LOG_DEBUG << "Can't find bmc D-Bus object!"; 2089 return; 2090 } 2091 // Assume only 1 bmc D-Bus object 2092 // Throw an error if there is more than 1 2093 if (subtree.size() > 1) 2094 { 2095 BMCWEB_LOG_DEBUG << "Found more than 1 bmc D-Bus object!"; 2096 messages::internalError(asyncResp->res); 2097 return; 2098 } 2099 2100 if (subtree[0].first.empty() || subtree[0].second.size() != 1) 2101 { 2102 BMCWEB_LOG_DEBUG << "Error getting bmc D-Bus object!"; 2103 messages::internalError(asyncResp->res); 2104 return; 2105 } 2106 2107 const std::string& path = subtree[0].first; 2108 const std::string& connectionName = subtree[0].second[0].first; 2109 2110 for (const auto& interfaceName : subtree[0].second[0].second) 2111 { 2112 if (interfaceName == 2113 "xyz.openbmc_project.Inventory.Decorator.Asset") 2114 { 2115 sdbusplus::asio::getAllProperties( 2116 *crow::connections::systemBus, connectionName, path, 2117 "xyz.openbmc_project.Inventory.Decorator.Asset", 2118 [asyncResp](const boost::system::error_code& ec2, 2119 const dbus::utility::DBusPropertiesMap& 2120 propertiesList) { 2121 if (ec2) 2122 { 2123 BMCWEB_LOG_DEBUG << "Can't get bmc asset!"; 2124 return; 2125 } 2126 2127 const std::string* partNumber = nullptr; 2128 const std::string* serialNumber = nullptr; 2129 const std::string* manufacturer = nullptr; 2130 const std::string* model = nullptr; 2131 const std::string* sparePartNumber = nullptr; 2132 2133 const bool success = sdbusplus::unpackPropertiesNoThrow( 2134 dbus_utils::UnpackErrorPrinter(), propertiesList, 2135 "PartNumber", partNumber, "SerialNumber", 2136 serialNumber, "Manufacturer", manufacturer, "Model", 2137 model, "SparePartNumber", sparePartNumber); 2138 2139 if (!success) 2140 { 2141 messages::internalError(asyncResp->res); 2142 return; 2143 } 2144 2145 if (partNumber != nullptr) 2146 { 2147 asyncResp->res.jsonValue["PartNumber"] = 2148 *partNumber; 2149 } 2150 2151 if (serialNumber != nullptr) 2152 { 2153 asyncResp->res.jsonValue["SerialNumber"] = 2154 *serialNumber; 2155 } 2156 2157 if (manufacturer != nullptr) 2158 { 2159 asyncResp->res.jsonValue["Manufacturer"] = 2160 *manufacturer; 2161 } 2162 2163 if (model != nullptr) 2164 { 2165 asyncResp->res.jsonValue["Model"] = *model; 2166 } 2167 2168 if (sparePartNumber != nullptr) 2169 { 2170 asyncResp->res.jsonValue["SparePartNumber"] = 2171 *sparePartNumber; 2172 } 2173 }); 2174 } 2175 else if (interfaceName == 2176 "xyz.openbmc_project.Inventory.Decorator.LocationCode") 2177 { 2178 getLocation(asyncResp, connectionName, path); 2179 } 2180 } 2181 }); 2182 }); 2183 2184 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/") 2185 .privileges(redfish::privileges::patchManager) 2186 .methods(boost::beast::http::verb::patch)( 2187 [&app](const crow::Request& req, 2188 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2189 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2190 { 2191 return; 2192 } 2193 std::optional<nlohmann::json> oem; 2194 std::optional<nlohmann::json> links; 2195 std::optional<std::string> datetime; 2196 2197 if (!json_util::readJsonPatch(req, asyncResp->res, "Oem", oem, 2198 "DateTime", datetime, "Links", links)) 2199 { 2200 return; 2201 } 2202 2203 if (oem) 2204 { 2205 #ifdef BMCWEB_ENABLE_REDFISH_OEM_MANAGER_FAN_DATA 2206 std::optional<nlohmann::json> openbmc; 2207 if (!redfish::json_util::readJson(*oem, asyncResp->res, "OpenBmc", 2208 openbmc)) 2209 { 2210 return; 2211 } 2212 if (openbmc) 2213 { 2214 std::optional<nlohmann::json> fan; 2215 if (!redfish::json_util::readJson(*openbmc, asyncResp->res, 2216 "Fan", fan)) 2217 { 2218 return; 2219 } 2220 if (fan) 2221 { 2222 auto pid = std::make_shared<SetPIDValues>(asyncResp, *fan); 2223 pid->run(); 2224 } 2225 } 2226 #else 2227 messages::propertyUnknown(asyncResp->res, "Oem"); 2228 return; 2229 #endif 2230 } 2231 if (links) 2232 { 2233 std::optional<nlohmann::json> activeSoftwareImage; 2234 if (!redfish::json_util::readJson(*links, asyncResp->res, 2235 "ActiveSoftwareImage", 2236 activeSoftwareImage)) 2237 { 2238 return; 2239 } 2240 if (activeSoftwareImage) 2241 { 2242 std::optional<std::string> odataId; 2243 if (!json_util::readJson(*activeSoftwareImage, asyncResp->res, 2244 "@odata.id", odataId)) 2245 { 2246 return; 2247 } 2248 2249 if (odataId) 2250 { 2251 setActiveFirmwareImage(asyncResp, *odataId); 2252 } 2253 } 2254 } 2255 if (datetime) 2256 { 2257 setDateTime(asyncResp, std::move(*datetime)); 2258 } 2259 }); 2260 } 2261 2262 inline void requestRoutesManagerCollection(App& app) 2263 { 2264 BMCWEB_ROUTE(app, "/redfish/v1/Managers/") 2265 .privileges(redfish::privileges::getManagerCollection) 2266 .methods(boost::beast::http::verb::get)( 2267 [&app](const crow::Request& req, 2268 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2269 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2270 { 2271 return; 2272 } 2273 // Collections don't include the static data added by SubRoute 2274 // because it has a duplicate entry for members 2275 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers"; 2276 asyncResp->res.jsonValue["@odata.type"] = 2277 "#ManagerCollection.ManagerCollection"; 2278 asyncResp->res.jsonValue["Name"] = "Manager Collection"; 2279 asyncResp->res.jsonValue["Members@odata.count"] = 1; 2280 nlohmann::json::array_t members; 2281 nlohmann::json& bmc = members.emplace_back(); 2282 bmc["@odata.id"] = "/redfish/v1/Managers/bmc"; 2283 asyncResp->res.jsonValue["Members"] = std::move(members); 2284 }); 2285 } 2286 } // namespace redfish 2287