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