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