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