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