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