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