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