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/sw_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 explicit GetPIDValues( 1172 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) : 1173 asyncResp(asyncRespIn) 1174 1175 {} 1176 1177 void run() 1178 { 1179 std::shared_ptr<GetPIDValues> self = shared_from_this(); 1180 1181 // get all configurations 1182 crow::connections::systemBus->async_method_call( 1183 [self]( 1184 const boost::system::error_code ec, 1185 const dbus::utility::MapperGetSubTreeResponse& subtreeLocal) { 1186 if (ec) 1187 { 1188 BMCWEB_LOG_ERROR << ec; 1189 messages::internalError(self->asyncResp->res); 1190 return; 1191 } 1192 self->subtree = subtreeLocal; 1193 }, 1194 "xyz.openbmc_project.ObjectMapper", 1195 "/xyz/openbmc_project/object_mapper", 1196 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, 1197 std::array<const char*, 4>{ 1198 pidConfigurationIface, pidZoneConfigurationIface, 1199 objectManagerIface, stepwiseConfigurationIface}); 1200 1201 // at the same time get the selected profile 1202 crow::connections::systemBus->async_method_call( 1203 [self]( 1204 const boost::system::error_code ec, 1205 const dbus::utility::MapperGetSubTreeResponse& subtreeLocal) { 1206 if (ec || subtreeLocal.empty()) 1207 { 1208 return; 1209 } 1210 if (subtreeLocal[0].second.size() != 1) 1211 { 1212 // invalid mapper response, should never happen 1213 BMCWEB_LOG_ERROR << "GetPIDValues: Mapper Error"; 1214 messages::internalError(self->asyncResp->res); 1215 return; 1216 } 1217 1218 const std::string& path = subtreeLocal[0].first; 1219 const std::string& owner = subtreeLocal[0].second[0].first; 1220 crow::connections::systemBus->async_method_call( 1221 [path, owner, 1222 self](const boost::system::error_code ec2, 1223 const dbus::utility::DBusPropertiesMap& resp) { 1224 if (ec2) 1225 { 1226 BMCWEB_LOG_ERROR 1227 << "GetPIDValues: Can't get thermalModeIface " << path; 1228 messages::internalError(self->asyncResp->res); 1229 return; 1230 } 1231 const std::string* current = nullptr; 1232 const std::vector<std::string>* supported = nullptr; 1233 for (const auto& [key, value] : resp) 1234 { 1235 if (key == "Current") 1236 { 1237 current = std::get_if<std::string>(&value); 1238 if (current == nullptr) 1239 { 1240 BMCWEB_LOG_ERROR 1241 << "GetPIDValues: thermal mode iface invalid " 1242 << path; 1243 messages::internalError(self->asyncResp->res); 1244 return; 1245 } 1246 } 1247 if (key == "Supported") 1248 { 1249 supported = 1250 std::get_if<std::vector<std::string>>(&value); 1251 if (supported == nullptr) 1252 { 1253 BMCWEB_LOG_ERROR 1254 << "GetPIDValues: thermal mode iface invalid" 1255 << path; 1256 messages::internalError(self->asyncResp->res); 1257 return; 1258 } 1259 } 1260 } 1261 if (current == nullptr || supported == nullptr) 1262 { 1263 BMCWEB_LOG_ERROR 1264 << "GetPIDValues: thermal mode iface invalid " << path; 1265 messages::internalError(self->asyncResp->res); 1266 return; 1267 } 1268 self->currentProfile = *current; 1269 self->supportedProfiles = *supported; 1270 }, 1271 owner, path, "org.freedesktop.DBus.Properties", "GetAll", 1272 thermalModeIface); 1273 }, 1274 "xyz.openbmc_project.ObjectMapper", 1275 "/xyz/openbmc_project/object_mapper", 1276 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, 1277 std::array<const char*, 1>{thermalModeIface}); 1278 } 1279 1280 ~GetPIDValues() 1281 { 1282 if (asyncResp->res.result() != boost::beast::http::status::ok) 1283 { 1284 return; 1285 } 1286 // create map of <connection, path to objMgr>> 1287 boost::container::flat_map<std::string, std::string> objectMgrPaths; 1288 boost::container::flat_set<std::string> calledConnections; 1289 for (const auto& pathGroup : subtree) 1290 { 1291 for (const auto& connectionGroup : pathGroup.second) 1292 { 1293 auto findConnection = 1294 calledConnections.find(connectionGroup.first); 1295 if (findConnection != calledConnections.end()) 1296 { 1297 break; 1298 } 1299 for (const std::string& interface : connectionGroup.second) 1300 { 1301 if (interface == objectManagerIface) 1302 { 1303 objectMgrPaths[connectionGroup.first] = pathGroup.first; 1304 } 1305 // this list is alphabetical, so we 1306 // should have found the objMgr by now 1307 if (interface == pidConfigurationIface || 1308 interface == pidZoneConfigurationIface || 1309 interface == stepwiseConfigurationIface) 1310 { 1311 auto findObjMgr = 1312 objectMgrPaths.find(connectionGroup.first); 1313 if (findObjMgr == objectMgrPaths.end()) 1314 { 1315 BMCWEB_LOG_DEBUG << connectionGroup.first 1316 << "Has no Object Manager"; 1317 continue; 1318 } 1319 1320 calledConnections.insert(connectionGroup.first); 1321 1322 asyncPopulatePid(findObjMgr->first, findObjMgr->second, 1323 currentProfile, supportedProfiles, 1324 asyncResp); 1325 break; 1326 } 1327 } 1328 } 1329 } 1330 } 1331 1332 GetPIDValues(const GetPIDValues&) = delete; 1333 GetPIDValues(GetPIDValues&&) = delete; 1334 GetPIDValues& operator=(const GetPIDValues&) = delete; 1335 GetPIDValues& operator=(GetPIDValues&&) = delete; 1336 1337 std::vector<std::string> supportedProfiles; 1338 std::string currentProfile; 1339 dbus::utility::MapperGetSubTreeResponse subtree; 1340 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1341 }; 1342 1343 struct SetPIDValues : std::enable_shared_from_this<SetPIDValues> 1344 { 1345 1346 SetPIDValues(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn, 1347 nlohmann::json& data) : 1348 asyncResp(asyncRespIn) 1349 { 1350 1351 std::optional<nlohmann::json> pidControllers; 1352 std::optional<nlohmann::json> fanControllers; 1353 std::optional<nlohmann::json> fanZones; 1354 std::optional<nlohmann::json> stepwiseControllers; 1355 1356 if (!redfish::json_util::readJson( 1357 data, asyncResp->res, "PidControllers", pidControllers, 1358 "FanControllers", fanControllers, "FanZones", fanZones, 1359 "StepwiseControllers", stepwiseControllers, "Profile", profile)) 1360 { 1361 BMCWEB_LOG_ERROR 1362 << "Illegal Property " 1363 << data.dump(2, ' ', true, 1364 nlohmann::json::error_handler_t::replace); 1365 return; 1366 } 1367 configuration.emplace_back("PidControllers", std::move(pidControllers)); 1368 configuration.emplace_back("FanControllers", std::move(fanControllers)); 1369 configuration.emplace_back("FanZones", std::move(fanZones)); 1370 configuration.emplace_back("StepwiseControllers", 1371 std::move(stepwiseControllers)); 1372 } 1373 1374 SetPIDValues(const SetPIDValues&) = delete; 1375 SetPIDValues(SetPIDValues&&) = delete; 1376 SetPIDValues& operator=(const SetPIDValues&) = delete; 1377 SetPIDValues& operator=(SetPIDValues&&) = delete; 1378 1379 void run() 1380 { 1381 if (asyncResp->res.result() != boost::beast::http::status::ok) 1382 { 1383 return; 1384 } 1385 1386 std::shared_ptr<SetPIDValues> self = shared_from_this(); 1387 1388 // todo(james): might make sense to do a mapper call here if this 1389 // interface gets more traction 1390 crow::connections::systemBus->async_method_call( 1391 [self](const boost::system::error_code ec, 1392 const dbus::utility::ManagedObjectType& mObj) { 1393 if (ec) 1394 { 1395 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager"; 1396 messages::internalError(self->asyncResp->res); 1397 return; 1398 } 1399 const std::array<const char*, 3> configurations = { 1400 pidConfigurationIface, pidZoneConfigurationIface, 1401 stepwiseConfigurationIface}; 1402 1403 for (const auto& [path, object] : mObj) 1404 { 1405 for (const auto& [interface, _] : object) 1406 { 1407 if (std::find(configurations.begin(), configurations.end(), 1408 interface) != configurations.end()) 1409 { 1410 self->objectCount++; 1411 break; 1412 } 1413 } 1414 } 1415 self->managedObj = mObj; 1416 }, 1417 "xyz.openbmc_project.EntityManager", "/", objectManagerIface, 1418 "GetManagedObjects"); 1419 1420 // at the same time get the profile information 1421 crow::connections::systemBus->async_method_call( 1422 [self](const boost::system::error_code ec, 1423 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1424 if (ec || subtree.empty()) 1425 { 1426 return; 1427 } 1428 if (subtree[0].second.empty()) 1429 { 1430 // invalid mapper response, should never happen 1431 BMCWEB_LOG_ERROR << "SetPIDValues: Mapper Error"; 1432 messages::internalError(self->asyncResp->res); 1433 return; 1434 } 1435 1436 const std::string& path = subtree[0].first; 1437 const std::string& owner = subtree[0].second[0].first; 1438 crow::connections::systemBus->async_method_call( 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 for (const auto& [key, value] : r) 1451 { 1452 if (key == "Current") 1453 { 1454 current = std::get_if<std::string>(&value); 1455 if (current == nullptr) 1456 { 1457 BMCWEB_LOG_ERROR 1458 << "SetPIDValues: thermal mode iface invalid " 1459 << path; 1460 messages::internalError(self->asyncResp->res); 1461 return; 1462 } 1463 } 1464 if (key == "Supported") 1465 { 1466 supported = 1467 std::get_if<std::vector<std::string>>(&value); 1468 if (supported == nullptr) 1469 { 1470 BMCWEB_LOG_ERROR 1471 << "SetPIDValues: thermal mode iface invalid" 1472 << path; 1473 messages::internalError(self->asyncResp->res); 1474 return; 1475 } 1476 } 1477 } 1478 if (current == nullptr || supported == nullptr) 1479 { 1480 BMCWEB_LOG_ERROR 1481 << "SetPIDValues: thermal mode iface invalid " << path; 1482 messages::internalError(self->asyncResp->res); 1483 return; 1484 } 1485 self->currentProfile = *current; 1486 self->supportedProfiles = *supported; 1487 self->profileConnection = owner; 1488 self->profilePath = path; 1489 }, 1490 owner, path, "org.freedesktop.DBus.Properties", "GetAll", 1491 thermalModeIface); 1492 }, 1493 "xyz.openbmc_project.ObjectMapper", 1494 "/xyz/openbmc_project/object_mapper", 1495 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, 1496 std::array<const char*, 1>{thermalModeIface}); 1497 } 1498 void pidSetDone() 1499 { 1500 if (asyncResp->res.result() != boost::beast::http::status::ok) 1501 { 1502 return; 1503 } 1504 std::shared_ptr<bmcweb::AsyncResp> response = asyncResp; 1505 if (profile) 1506 { 1507 if (std::find(supportedProfiles.begin(), supportedProfiles.end(), 1508 *profile) == supportedProfiles.end()) 1509 { 1510 messages::actionParameterUnknown(response->res, "Profile", 1511 *profile); 1512 return; 1513 } 1514 currentProfile = *profile; 1515 crow::connections::systemBus->async_method_call( 1516 [response](const boost::system::error_code ec) { 1517 if (ec) 1518 { 1519 BMCWEB_LOG_ERROR << "Error patching profile" << ec; 1520 messages::internalError(response->res); 1521 } 1522 }, 1523 profileConnection, profilePath, 1524 "org.freedesktop.DBus.Properties", "Set", thermalModeIface, 1525 "Current", dbus::utility::DbusVariantType(*profile)); 1526 } 1527 1528 for (auto& containerPair : configuration) 1529 { 1530 auto& container = containerPair.second; 1531 if (!container) 1532 { 1533 continue; 1534 } 1535 BMCWEB_LOG_DEBUG << *container; 1536 1537 std::string& type = containerPair.first; 1538 1539 for (nlohmann::json::iterator it = container->begin(); 1540 it != container->end(); ++it) 1541 { 1542 const auto& name = it.key(); 1543 BMCWEB_LOG_DEBUG << "looking for " << name; 1544 1545 auto pathItr = 1546 std::find_if(managedObj.begin(), managedObj.end(), 1547 [&name](const auto& obj) { 1548 return boost::algorithm::ends_with(obj.first.str, 1549 "/" + name); 1550 }); 1551 dbus::utility::DBusPropertiesMap output; 1552 1553 output.reserve(16); // The pid interface length 1554 1555 // determines if we're patching entity-manager or 1556 // creating a new object 1557 bool createNewObject = (pathItr == managedObj.end()); 1558 BMCWEB_LOG_DEBUG << "Found = " << !createNewObject; 1559 1560 std::string iface; 1561 /* 1562 if (type == "PidControllers" || type == "FanControllers") 1563 { 1564 iface = pidConfigurationIface; 1565 if (!createNewObject && 1566 pathItr->second.find(pidConfigurationIface) == 1567 pathItr->second.end()) 1568 { 1569 createNewObject = true; 1570 } 1571 } 1572 else if (type == "FanZones") 1573 { 1574 iface = pidZoneConfigurationIface; 1575 if (!createNewObject && 1576 pathItr->second.find(pidZoneConfigurationIface) == 1577 pathItr->second.end()) 1578 { 1579 1580 createNewObject = true; 1581 } 1582 } 1583 else if (type == "StepwiseControllers") 1584 { 1585 iface = stepwiseConfigurationIface; 1586 if (!createNewObject && 1587 pathItr->second.find(stepwiseConfigurationIface) == 1588 pathItr->second.end()) 1589 { 1590 createNewObject = true; 1591 } 1592 }*/ 1593 1594 if (createNewObject && it.value() == nullptr) 1595 { 1596 // can't delete a non-existent object 1597 messages::propertyValueNotInList(response->res, 1598 it.value().dump(), name); 1599 continue; 1600 } 1601 1602 std::string path; 1603 if (pathItr != managedObj.end()) 1604 { 1605 path = pathItr->first.str; 1606 } 1607 1608 BMCWEB_LOG_DEBUG << "Create new = " << createNewObject << "\n"; 1609 1610 // arbitrary limit to avoid attacks 1611 constexpr const size_t controllerLimit = 500; 1612 if (createNewObject && objectCount >= controllerLimit) 1613 { 1614 messages::resourceExhaustion(response->res, type); 1615 continue; 1616 } 1617 output.emplace_back("Name", 1618 boost::replace_all_copy(name, "_", " ")); 1619 1620 std::string chassis; 1621 CreatePIDRet ret = createPidInterface( 1622 response, type, it, path, managedObj, createNewObject, 1623 output, chassis, currentProfile); 1624 if (ret == CreatePIDRet::fail) 1625 { 1626 return; 1627 } 1628 if (ret == CreatePIDRet::del) 1629 { 1630 continue; 1631 } 1632 1633 if (!createNewObject) 1634 { 1635 for (const auto& property : output) 1636 { 1637 crow::connections::systemBus->async_method_call( 1638 [response, 1639 propertyName{std::string(property.first)}]( 1640 const boost::system::error_code ec) { 1641 if (ec) 1642 { 1643 BMCWEB_LOG_ERROR << "Error patching " 1644 << propertyName << ": " << ec; 1645 messages::internalError(response->res); 1646 return; 1647 } 1648 messages::success(response->res); 1649 }, 1650 "xyz.openbmc_project.EntityManager", path, 1651 "org.freedesktop.DBus.Properties", "Set", iface, 1652 property.first, property.second); 1653 } 1654 } 1655 else 1656 { 1657 if (chassis.empty()) 1658 { 1659 BMCWEB_LOG_ERROR << "Failed to get chassis from config"; 1660 messages::internalError(response->res); 1661 return; 1662 } 1663 1664 bool foundChassis = false; 1665 for (const auto& obj : managedObj) 1666 { 1667 if (boost::algorithm::ends_with(obj.first.str, chassis)) 1668 { 1669 chassis = obj.first.str; 1670 foundChassis = true; 1671 break; 1672 } 1673 } 1674 if (!foundChassis) 1675 { 1676 BMCWEB_LOG_ERROR << "Failed to find chassis on dbus"; 1677 messages::resourceMissingAtURI( 1678 response->res, 1679 crow::utility::urlFromPieces("redfish", "v1", 1680 "Chassis", chassis)); 1681 return; 1682 } 1683 1684 crow::connections::systemBus->async_method_call( 1685 [response](const boost::system::error_code ec) { 1686 if (ec) 1687 { 1688 BMCWEB_LOG_ERROR << "Error Adding Pid Object " 1689 << ec; 1690 messages::internalError(response->res); 1691 return; 1692 } 1693 messages::success(response->res); 1694 }, 1695 "xyz.openbmc_project.EntityManager", chassis, 1696 "xyz.openbmc_project.AddObject", "AddObject", output); 1697 } 1698 } 1699 } 1700 } 1701 1702 ~SetPIDValues() 1703 { 1704 try 1705 { 1706 pidSetDone(); 1707 } 1708 catch (...) 1709 { 1710 BMCWEB_LOG_CRITICAL << "pidSetDone threw exception"; 1711 } 1712 } 1713 1714 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1715 std::vector<std::pair<std::string, std::optional<nlohmann::json>>> 1716 configuration; 1717 std::optional<std::string> profile; 1718 dbus::utility::ManagedObjectType managedObj; 1719 std::vector<std::string> supportedProfiles; 1720 std::string currentProfile; 1721 std::string profileConnection; 1722 std::string profilePath; 1723 size_t objectCount = 0; 1724 }; 1725 1726 /** 1727 * @brief Retrieves BMC manager location data over DBus 1728 * 1729 * @param[in] aResp Shared pointer for completing asynchronous calls 1730 * @param[in] connectionName - service name 1731 * @param[in] path - object path 1732 * @return none 1733 */ 1734 inline void getLocation(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1735 const std::string& connectionName, 1736 const std::string& path) 1737 { 1738 BMCWEB_LOG_DEBUG << "Get BMC manager Location data."; 1739 1740 sdbusplus::asio::getProperty<std::string>( 1741 *crow::connections::systemBus, connectionName, path, 1742 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 1743 [aResp](const boost::system::error_code ec, 1744 const std::string& property) { 1745 if (ec) 1746 { 1747 BMCWEB_LOG_DEBUG << "DBUS response error for " 1748 "Location"; 1749 messages::internalError(aResp->res); 1750 return; 1751 } 1752 1753 aResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 1754 property; 1755 }); 1756 } 1757 // avoid name collision systems.hpp 1758 inline void 1759 managerGetLastResetTime(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1760 { 1761 BMCWEB_LOG_DEBUG << "Getting Manager Last Reset Time"; 1762 1763 sdbusplus::asio::getProperty<uint64_t>( 1764 *crow::connections::systemBus, "xyz.openbmc_project.State.BMC", 1765 "/xyz/openbmc_project/state/bmc0", "xyz.openbmc_project.State.BMC", 1766 "LastRebootTime", 1767 [aResp](const boost::system::error_code ec, 1768 const uint64_t lastResetTime) { 1769 if (ec) 1770 { 1771 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec; 1772 return; 1773 } 1774 1775 // LastRebootTime is epoch time, in milliseconds 1776 // https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19 1777 uint64_t lastResetTimeStamp = lastResetTime / 1000; 1778 1779 // Convert to ISO 8601 standard 1780 aResp->res.jsonValue["LastResetTime"] = 1781 crow::utility::getDateTimeUint(lastResetTimeStamp); 1782 }); 1783 } 1784 1785 /** 1786 * @brief Set the running firmware image 1787 * 1788 * @param[i,o] aResp - Async response object 1789 * @param[i] runningFirmwareTarget - Image to make the running image 1790 * 1791 * @return void 1792 */ 1793 inline void 1794 setActiveFirmwareImage(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1795 const std::string& runningFirmwareTarget) 1796 { 1797 // Get the Id from /redfish/v1/UpdateService/FirmwareInventory/<Id> 1798 std::string::size_type idPos = runningFirmwareTarget.rfind('/'); 1799 if (idPos == std::string::npos) 1800 { 1801 messages::propertyValueNotInList(aResp->res, runningFirmwareTarget, 1802 "@odata.id"); 1803 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!"; 1804 return; 1805 } 1806 idPos++; 1807 if (idPos >= runningFirmwareTarget.size()) 1808 { 1809 messages::propertyValueNotInList(aResp->res, runningFirmwareTarget, 1810 "@odata.id"); 1811 BMCWEB_LOG_DEBUG << "Invalid firmware ID."; 1812 return; 1813 } 1814 std::string firmwareId = runningFirmwareTarget.substr(idPos); 1815 1816 // Make sure the image is valid before setting priority 1817 crow::connections::systemBus->async_method_call( 1818 [aResp, firmwareId, 1819 runningFirmwareTarget](const boost::system::error_code ec, 1820 dbus::utility::ManagedObjectType& subtree) { 1821 if (ec) 1822 { 1823 BMCWEB_LOG_DEBUG << "D-Bus response error getting objects."; 1824 messages::internalError(aResp->res); 1825 return; 1826 } 1827 1828 if (subtree.empty()) 1829 { 1830 BMCWEB_LOG_DEBUG << "Can't find image!"; 1831 messages::internalError(aResp->res); 1832 return; 1833 } 1834 1835 bool foundImage = false; 1836 for (auto& object : subtree) 1837 { 1838 const std::string& path = 1839 static_cast<const std::string&>(object.first); 1840 std::size_t idPos2 = path.rfind('/'); 1841 1842 if (idPos2 == std::string::npos) 1843 { 1844 continue; 1845 } 1846 1847 idPos2++; 1848 if (idPos2 >= path.size()) 1849 { 1850 continue; 1851 } 1852 1853 if (path.substr(idPos2) == firmwareId) 1854 { 1855 foundImage = true; 1856 break; 1857 } 1858 } 1859 1860 if (!foundImage) 1861 { 1862 messages::propertyValueNotInList(aResp->res, runningFirmwareTarget, 1863 "@odata.id"); 1864 BMCWEB_LOG_DEBUG << "Invalid firmware ID."; 1865 return; 1866 } 1867 1868 BMCWEB_LOG_DEBUG << "Setting firmware version " << firmwareId 1869 << " to priority 0."; 1870 1871 // Only support Immediate 1872 // An addition could be a Redfish Setting like 1873 // ActiveSoftwareImageApplyTime and support OnReset 1874 crow::connections::systemBus->async_method_call( 1875 [aResp](const boost::system::error_code ec2) { 1876 if (ec2) 1877 { 1878 BMCWEB_LOG_DEBUG << "D-Bus response error setting."; 1879 messages::internalError(aResp->res); 1880 return; 1881 } 1882 doBMCGracefulRestart(aResp); 1883 }, 1884 1885 "xyz.openbmc_project.Software.BMC.Updater", 1886 "/xyz/openbmc_project/software/" + firmwareId, 1887 "org.freedesktop.DBus.Properties", "Set", 1888 "xyz.openbmc_project.Software.RedundancyPriority", "Priority", 1889 dbus::utility::DbusVariantType(static_cast<uint8_t>(0))); 1890 }, 1891 "xyz.openbmc_project.Software.BMC.Updater", 1892 "/xyz/openbmc_project/software", "org.freedesktop.DBus.ObjectManager", 1893 "GetManagedObjects"); 1894 } 1895 1896 inline void setDateTime(std::shared_ptr<bmcweb::AsyncResp> aResp, 1897 std::string datetime) 1898 { 1899 BMCWEB_LOG_DEBUG << "Set date time: " << datetime; 1900 1901 std::stringstream stream(datetime); 1902 // Convert from ISO 8601 to boost local_time 1903 // (BMC only has time in UTC) 1904 boost::posix_time::ptime posixTime; 1905 boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1)); 1906 // Facet gets deleted with the stringsteam 1907 auto ifc = std::make_unique<boost::local_time::local_time_input_facet>( 1908 "%Y-%m-%d %H:%M:%S%F %ZP"); 1909 stream.imbue(std::locale(stream.getloc(), ifc.release())); 1910 1911 boost::local_time::local_date_time ldt(boost::local_time::not_a_date_time); 1912 1913 if (stream >> ldt) 1914 { 1915 posixTime = ldt.utc_time(); 1916 boost::posix_time::time_duration dur = posixTime - epoch; 1917 uint64_t durMicroSecs = static_cast<uint64_t>(dur.total_microseconds()); 1918 crow::connections::systemBus->async_method_call( 1919 [aResp{std::move(aResp)}, datetime{std::move(datetime)}]( 1920 const boost::system::error_code ec) { 1921 if (ec) 1922 { 1923 BMCWEB_LOG_DEBUG << "Failed to set elapsed time. " 1924 "DBUS response error " 1925 << ec; 1926 messages::internalError(aResp->res); 1927 return; 1928 } 1929 aResp->res.jsonValue["DateTime"] = datetime; 1930 }, 1931 "xyz.openbmc_project.Time.Manager", "/xyz/openbmc_project/time/bmc", 1932 "org.freedesktop.DBus.Properties", "Set", 1933 "xyz.openbmc_project.Time.EpochTime", "Elapsed", 1934 dbus::utility::DbusVariantType(durMicroSecs)); 1935 } 1936 else 1937 { 1938 messages::propertyValueFormatError(aResp->res, datetime, "DateTime"); 1939 return; 1940 } 1941 } 1942 1943 inline void requestRoutesManager(App& app) 1944 { 1945 std::string uuid = persistent_data::getConfig().systemUuid; 1946 1947 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/") 1948 .privileges(redfish::privileges::getManager) 1949 .methods(boost::beast::http::verb::get)( 1950 [&app, uuid](const crow::Request& req, 1951 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1952 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1953 { 1954 return; 1955 } 1956 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc"; 1957 asyncResp->res.jsonValue["@odata.type"] = "#Manager.v1_11_0.Manager"; 1958 asyncResp->res.jsonValue["Id"] = "bmc"; 1959 asyncResp->res.jsonValue["Name"] = "OpenBmc Manager"; 1960 asyncResp->res.jsonValue["Description"] = 1961 "Baseboard Management Controller"; 1962 asyncResp->res.jsonValue["PowerState"] = "On"; 1963 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 1964 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 1965 1966 asyncResp->res.jsonValue["ManagerType"] = "BMC"; 1967 asyncResp->res.jsonValue["UUID"] = systemd_utils::getUuid(); 1968 asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid; 1969 asyncResp->res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model 1970 1971 asyncResp->res.jsonValue["LogServices"]["@odata.id"] = 1972 "/redfish/v1/Managers/bmc/LogServices"; 1973 asyncResp->res.jsonValue["NetworkProtocol"]["@odata.id"] = 1974 "/redfish/v1/Managers/bmc/NetworkProtocol"; 1975 asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = 1976 "/redfish/v1/Managers/bmc/EthernetInterfaces"; 1977 1978 #ifdef BMCWEB_ENABLE_VM_NBDPROXY 1979 asyncResp->res.jsonValue["VirtualMedia"]["@odata.id"] = 1980 "/redfish/v1/Managers/bmc/VirtualMedia"; 1981 #endif // BMCWEB_ENABLE_VM_NBDPROXY 1982 1983 // default oem data 1984 nlohmann::json& oem = asyncResp->res.jsonValue["Oem"]; 1985 nlohmann::json& oemOpenbmc = oem["OpenBmc"]; 1986 oem["@odata.type"] = "#OemManager.Oem"; 1987 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem"; 1988 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc"; 1989 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc"; 1990 1991 nlohmann::json::object_t certificates; 1992 certificates["@odata.id"] = 1993 "/redfish/v1/Managers/bmc/Truststore/Certificates"; 1994 oemOpenbmc["Certificates"] = std::move(certificates); 1995 1996 // Manager.Reset (an action) can be many values, OpenBMC only 1997 // supports BMC reboot. 1998 nlohmann::json& managerReset = 1999 asyncResp->res.jsonValue["Actions"]["#Manager.Reset"]; 2000 managerReset["target"] = 2001 "/redfish/v1/Managers/bmc/Actions/Manager.Reset"; 2002 managerReset["@Redfish.ActionInfo"] = 2003 "/redfish/v1/Managers/bmc/ResetActionInfo"; 2004 2005 // ResetToDefaults (Factory Reset) has values like 2006 // PreserveNetworkAndUsers and PreserveNetwork that aren't supported 2007 // on OpenBMC 2008 nlohmann::json& resetToDefaults = 2009 asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"]; 2010 resetToDefaults["target"] = 2011 "/redfish/v1/Managers/bmc/Actions/Manager.ResetToDefaults"; 2012 resetToDefaults["ResetType@Redfish.AllowableValues"] = {"ResetAll"}; 2013 2014 std::pair<std::string, std::string> redfishDateTimeOffset = 2015 crow::utility::getDateTimeOffsetNow(); 2016 2017 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 2018 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 2019 redfishDateTimeOffset.second; 2020 2021 // TODO (Gunnar): Remove these one day since moved to ComputerSystem 2022 // Still used by OCP profiles 2023 // https://github.com/opencomputeproject/OCP-Profiles/issues/23 2024 // Fill in SerialConsole info 2025 asyncResp->res.jsonValue["SerialConsole"]["ServiceEnabled"] = true; 2026 asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15; 2027 asyncResp->res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = { 2028 "IPMI", "SSH"}; 2029 #ifdef BMCWEB_ENABLE_KVM 2030 // Fill in GraphicalConsole info 2031 asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true; 2032 asyncResp->res.jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 2033 4; 2034 asyncResp->res 2035 .jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = {"KVMIP"}; 2036 #endif // BMCWEB_ENABLE_KVM 2037 2038 asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1; 2039 2040 nlohmann::json::array_t managerForServers; 2041 nlohmann::json::object_t manager; 2042 manager["@odata.id"] = "/redfish/v1/Systems/system"; 2043 managerForServers.push_back(std::move(manager)); 2044 2045 asyncResp->res.jsonValue["Links"]["ManagerForServers"] = 2046 std::move(managerForServers); 2047 2048 auto health = std::make_shared<HealthPopulate>(asyncResp); 2049 health->isManagersHealth = true; 2050 health->populate(); 2051 2052 sw_util::populateSoftwareInformation(asyncResp, sw_util::bmcPurpose, 2053 "FirmwareVersion", true); 2054 2055 managerGetLastResetTime(asyncResp); 2056 2057 auto pids = std::make_shared<GetPIDValues>(asyncResp); 2058 pids->run(); 2059 2060 getMainChassisId(asyncResp, 2061 [](const std::string& chassisId, 2062 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { 2063 aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1; 2064 nlohmann::json::array_t managerForChassis; 2065 nlohmann::json::object_t managerObj; 2066 managerObj["@odata.id"] = "/redfish/v1/Chassis/" + chassisId; 2067 managerForChassis.push_back(std::move(managerObj)); 2068 aRsp->res.jsonValue["Links"]["ManagerForChassis"] = 2069 std::move(managerForChassis); 2070 aRsp->res.jsonValue["Links"]["ManagerInChassis"]["@odata.id"] = 2071 "/redfish/v1/Chassis/" + chassisId; 2072 }); 2073 2074 static bool started = false; 2075 2076 if (!started) 2077 { 2078 sdbusplus::asio::getProperty<double>( 2079 *crow::connections::systemBus, "org.freedesktop.systemd1", 2080 "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", 2081 "Progress", 2082 [asyncResp](const boost::system::error_code ec, 2083 const double& val) { 2084 if (ec) 2085 { 2086 BMCWEB_LOG_ERROR << "Error while getting progress"; 2087 messages::internalError(asyncResp->res); 2088 return; 2089 } 2090 if (val < 1.0) 2091 { 2092 asyncResp->res.jsonValue["Status"]["State"] = "Starting"; 2093 started = true; 2094 } 2095 }); 2096 } 2097 2098 crow::connections::systemBus->async_method_call( 2099 [asyncResp]( 2100 const boost::system::error_code ec, 2101 const dbus::utility::MapperGetSubTreeResponse& subtree) { 2102 if (ec) 2103 { 2104 BMCWEB_LOG_DEBUG << "D-Bus response error on GetSubTree " << ec; 2105 return; 2106 } 2107 if (subtree.empty()) 2108 { 2109 BMCWEB_LOG_DEBUG << "Can't find bmc D-Bus object!"; 2110 return; 2111 } 2112 // Assume only 1 bmc D-Bus object 2113 // Throw an error if there is more than 1 2114 if (subtree.size() > 1) 2115 { 2116 BMCWEB_LOG_DEBUG << "Found more than 1 bmc D-Bus object!"; 2117 messages::internalError(asyncResp->res); 2118 return; 2119 } 2120 2121 if (subtree[0].first.empty() || subtree[0].second.size() != 1) 2122 { 2123 BMCWEB_LOG_DEBUG << "Error getting bmc D-Bus object!"; 2124 messages::internalError(asyncResp->res); 2125 return; 2126 } 2127 2128 const std::string& path = subtree[0].first; 2129 const std::string& connectionName = subtree[0].second[0].first; 2130 2131 for (const auto& interfaceName : subtree[0].second[0].second) 2132 { 2133 if (interfaceName == 2134 "xyz.openbmc_project.Inventory.Decorator.Asset") 2135 { 2136 crow::connections::systemBus->async_method_call( 2137 [asyncResp](const boost::system::error_code ec2, 2138 const dbus::utility::DBusPropertiesMap& 2139 propertiesList) { 2140 if (ec2) 2141 { 2142 BMCWEB_LOG_DEBUG << "Can't get bmc asset!"; 2143 return; 2144 } 2145 for (const std::pair<std::string, 2146 dbus::utility::DbusVariantType>& 2147 property : propertiesList) 2148 { 2149 const std::string& propertyName = property.first; 2150 2151 if ((propertyName == "PartNumber") || 2152 (propertyName == "SerialNumber") || 2153 (propertyName == "Manufacturer") || 2154 (propertyName == "Model") || 2155 (propertyName == "SparePartNumber")) 2156 { 2157 const std::string* value = 2158 std::get_if<std::string>(&property.second); 2159 if (value == nullptr) 2160 { 2161 // illegal property 2162 messages::internalError(asyncResp->res); 2163 return; 2164 } 2165 asyncResp->res.jsonValue[propertyName] = *value; 2166 } 2167 } 2168 }, 2169 connectionName, path, "org.freedesktop.DBus.Properties", 2170 "GetAll", 2171 "xyz.openbmc_project.Inventory.Decorator.Asset"); 2172 } 2173 else if (interfaceName == 2174 "xyz.openbmc_project.Inventory.Decorator.LocationCode") 2175 { 2176 getLocation(asyncResp, connectionName, path); 2177 } 2178 } 2179 }, 2180 "xyz.openbmc_project.ObjectMapper", 2181 "/xyz/openbmc_project/object_mapper", 2182 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 2183 "/xyz/openbmc_project/inventory", int32_t(0), 2184 std::array<const char*, 1>{ 2185 "xyz.openbmc_project.Inventory.Item.Bmc"}); 2186 }); 2187 2188 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/") 2189 .privileges(redfish::privileges::patchManager) 2190 .methods(boost::beast::http::verb::patch)( 2191 [&app](const crow::Request& req, 2192 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2193 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2194 { 2195 return; 2196 } 2197 std::optional<nlohmann::json> oem; 2198 std::optional<nlohmann::json> links; 2199 std::optional<std::string> datetime; 2200 2201 if (!json_util::readJsonPatch(req, asyncResp->res, "Oem", oem, 2202 "DateTime", datetime, "Links", links)) 2203 { 2204 return; 2205 } 2206 2207 if (oem) 2208 { 2209 std::optional<nlohmann::json> openbmc; 2210 if (!redfish::json_util::readJson(*oem, asyncResp->res, "OpenBmc", 2211 openbmc)) 2212 { 2213 BMCWEB_LOG_ERROR 2214 << "Illegal Property " 2215 << oem->dump(2, ' ', true, 2216 nlohmann::json::error_handler_t::replace); 2217 return; 2218 } 2219 if (openbmc) 2220 { 2221 std::optional<nlohmann::json> fan; 2222 if (!redfish::json_util::readJson(*openbmc, asyncResp->res, 2223 "Fan", fan)) 2224 { 2225 BMCWEB_LOG_ERROR 2226 << "Illegal Property " 2227 << openbmc->dump( 2228 2, ' ', true, 2229 nlohmann::json::error_handler_t::replace); 2230 return; 2231 } 2232 if (fan) 2233 { 2234 auto pid = std::make_shared<SetPIDValues>(asyncResp, *fan); 2235 pid->run(); 2236 } 2237 } 2238 } 2239 if (links) 2240 { 2241 std::optional<nlohmann::json> activeSoftwareImage; 2242 if (!redfish::json_util::readJson(*links, asyncResp->res, 2243 "ActiveSoftwareImage", 2244 activeSoftwareImage)) 2245 { 2246 return; 2247 } 2248 if (activeSoftwareImage) 2249 { 2250 std::optional<std::string> odataId; 2251 if (!json_util::readJson(*activeSoftwareImage, asyncResp->res, 2252 "@odata.id", odataId)) 2253 { 2254 return; 2255 } 2256 2257 if (odataId) 2258 { 2259 setActiveFirmwareImage(asyncResp, *odataId); 2260 } 2261 } 2262 } 2263 if (datetime) 2264 { 2265 setDateTime(asyncResp, std::move(*datetime)); 2266 } 2267 }); 2268 } 2269 2270 inline void requestRoutesManagerCollection(App& app) 2271 { 2272 BMCWEB_ROUTE(app, "/redfish/v1/Managers/") 2273 .privileges(redfish::privileges::getManagerCollection) 2274 .methods(boost::beast::http::verb::get)( 2275 [&app](const crow::Request& req, 2276 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2277 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2278 { 2279 return; 2280 } 2281 // Collections don't include the static data added by SubRoute 2282 // because it has a duplicate entry for members 2283 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers"; 2284 asyncResp->res.jsonValue["@odata.type"] = 2285 "#ManagerCollection.ManagerCollection"; 2286 asyncResp->res.jsonValue["Name"] = "Manager Collection"; 2287 asyncResp->res.jsonValue["Members@odata.count"] = 1; 2288 nlohmann::json::array_t members; 2289 nlohmann::json& bmc = members.emplace_back(); 2290 bmc["@odata.id"] = "/redfish/v1/Managers/bmc"; 2291 asyncResp->res.jsonValue["Members"] = std::move(members); 2292 }); 2293 } 2294 } // namespace redfish 2295