1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "app.hpp" 19 #include "dbus_utility.hpp" 20 #include "health.hpp" 21 #include "query.hpp" 22 #include "redfish_util.hpp" 23 #include "registries/privilege_registry.hpp" 24 #include "utils/sw_utils.hpp" 25 #include "utils/systemd_utils.hpp" 26 27 #include <boost/date_time.hpp> 28 29 #include <algorithm> 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 std::replace(input.begin(), input.end(), '_', ' '); 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 = value; 732 std::replace(escaped.begin(), escaped.end(), '_', ' '); 733 escaped = "/" + escaped; 734 auto it = std::find_if(managedObj.begin(), managedObj.end(), 735 [&escaped](const auto& obj) { 736 if (boost::algorithm::ends_with(obj.first.str, escaped)) 737 { 738 BMCWEB_LOG_DEBUG << "Matched " << obj.first.str << "\n"; 739 return true; 740 } 741 return false; 742 }); 743 744 if (it == managedObj.end()) 745 { 746 return nullptr; 747 } 748 // 5 comes from <chassis-name> being the 5th element 749 // /xyz/openbmc_project/inventory/system/chassis/<chassis-name> 750 if (dbus::utility::getNthStringFromPath(it->first.str, 5, chassis)) 751 { 752 return &(*it); 753 } 754 755 return nullptr; 756 } 757 758 inline CreatePIDRet createPidInterface( 759 const std::shared_ptr<bmcweb::AsyncResp>& response, const std::string& type, 760 const nlohmann::json::iterator& it, const std::string& path, 761 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject, 762 dbus::utility::DBusPropertiesMap& output, std::string& chassis, 763 const std::string& profile) 764 { 765 766 // common deleter 767 if (it.value() == nullptr) 768 { 769 std::string iface; 770 if (type == "PidControllers" || type == "FanControllers") 771 { 772 iface = pidConfigurationIface; 773 } 774 else if (type == "FanZones") 775 { 776 iface = pidZoneConfigurationIface; 777 } 778 else if (type == "StepwiseControllers") 779 { 780 iface = stepwiseConfigurationIface; 781 } 782 else 783 { 784 BMCWEB_LOG_ERROR << "Illegal Type " << type; 785 messages::propertyUnknown(response->res, type); 786 return CreatePIDRet::fail; 787 } 788 789 BMCWEB_LOG_DEBUG << "del " << path << " " << iface << "\n"; 790 // delete interface 791 crow::connections::systemBus->async_method_call( 792 [response, path](const boost::system::error_code ec) { 793 if (ec) 794 { 795 BMCWEB_LOG_ERROR << "Error patching " << path << ": " << ec; 796 messages::internalError(response->res); 797 return; 798 } 799 messages::success(response->res); 800 }, 801 "xyz.openbmc_project.EntityManager", path, iface, "Delete"); 802 return CreatePIDRet::del; 803 } 804 805 const dbus::utility::ManagedObjectType::value_type* managedItem = nullptr; 806 if (!createNewObject) 807 { 808 // if we aren't creating a new object, we should be able to find it on 809 // d-bus 810 managedItem = findChassis(managedObj, it.key(), chassis); 811 if (managedItem == nullptr) 812 { 813 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch"; 814 messages::invalidObject(response->res, 815 crow::utility::urlFromPieces( 816 "redfish", "v1", "Chassis", chassis)); 817 return CreatePIDRet::fail; 818 } 819 } 820 821 if (!profile.empty() && 822 (type == "PidControllers" || type == "FanControllers" || 823 type == "StepwiseControllers")) 824 { 825 if (managedItem == nullptr) 826 { 827 output.emplace_back("Profiles", std::vector<std::string>{profile}); 828 } 829 else 830 { 831 std::string interface; 832 if (type == "StepwiseControllers") 833 { 834 interface = stepwiseConfigurationIface; 835 } 836 else 837 { 838 interface = pidConfigurationIface; 839 } 840 bool ifaceFound = false; 841 for (const auto& iface : managedItem->second) 842 { 843 if (iface.first == interface) 844 { 845 ifaceFound = true; 846 for (const auto& prop : iface.second) 847 { 848 if (prop.first == "Profiles") 849 { 850 const std::vector<std::string>* curProfiles = 851 std::get_if<std::vector<std::string>>( 852 &(prop.second)); 853 if (curProfiles == nullptr) 854 { 855 BMCWEB_LOG_ERROR 856 << "Illegal profiles in managed object"; 857 messages::internalError(response->res); 858 return CreatePIDRet::fail; 859 } 860 if (std::find(curProfiles->begin(), 861 curProfiles->end(), 862 profile) == curProfiles->end()) 863 { 864 std::vector<std::string> newProfiles = 865 *curProfiles; 866 newProfiles.push_back(profile); 867 output.emplace_back("Profiles", newProfiles); 868 } 869 } 870 } 871 } 872 } 873 874 if (!ifaceFound) 875 { 876 BMCWEB_LOG_ERROR 877 << "Failed to find interface in managed object"; 878 messages::internalError(response->res); 879 return CreatePIDRet::fail; 880 } 881 } 882 } 883 884 if (type == "PidControllers" || type == "FanControllers") 885 { 886 if (createNewObject) 887 { 888 output.emplace_back("Class", 889 type == "PidControllers" ? "temp" : "fan"); 890 output.emplace_back("Type", "Pid"); 891 } 892 893 std::optional<std::vector<nlohmann::json>> zones; 894 std::optional<std::vector<std::string>> inputs; 895 std::optional<std::vector<std::string>> outputs; 896 std::map<std::string, std::optional<double>> doubles; 897 std::optional<std::string> setpointOffset; 898 if (!redfish::json_util::readJson( 899 it.value(), response->res, "Inputs", inputs, "Outputs", outputs, 900 "Zones", zones, "FFGainCoefficient", 901 doubles["FFGainCoefficient"], "FFOffCoefficient", 902 doubles["FFOffCoefficient"], "ICoefficient", 903 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"], 904 "ILimitMin", doubles["ILimitMin"], "OutLimitMax", 905 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"], 906 "PCoefficient", doubles["PCoefficient"], "SetPoint", 907 doubles["SetPoint"], "SetPointOffset", setpointOffset, 908 "SlewNeg", doubles["SlewNeg"], "SlewPos", doubles["SlewPos"], 909 "PositiveHysteresis", doubles["PositiveHysteresis"], 910 "NegativeHysteresis", doubles["NegativeHysteresis"])) 911 { 912 BMCWEB_LOG_ERROR 913 << "Illegal Property " 914 << it.value().dump(2, ' ', true, 915 nlohmann::json::error_handler_t::replace); 916 return CreatePIDRet::fail; 917 } 918 if (zones) 919 { 920 std::vector<std::string> zonesStr; 921 if (!getZonesFromJsonReq(response, *zones, zonesStr)) 922 { 923 BMCWEB_LOG_ERROR << "Illegal Zones"; 924 return CreatePIDRet::fail; 925 } 926 if (chassis.empty() && 927 findChassis(managedObj, zonesStr[0], chassis) == nullptr) 928 { 929 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch"; 930 messages::invalidObject( 931 response->res, crow::utility::urlFromPieces( 932 "redfish", "v1", "Chassis", chassis)); 933 return CreatePIDRet::fail; 934 } 935 output.emplace_back("Zones", std::move(zonesStr)); 936 } 937 if (inputs || outputs) 938 { 939 std::array< 940 std::reference_wrapper<std::optional<std::vector<std::string>>>, 941 2> 942 containers = {inputs, outputs}; 943 size_t index = 0; 944 for (std::optional<std::vector<std::string>>& container : 945 containers) 946 { 947 if (!container) 948 { 949 index++; 950 continue; 951 } 952 for (std::string& value : *container) 953 { 954 std::replace(value.begin(), value.end(), '_', ' '); 955 } 956 std::string key; 957 if (index == 0) 958 { 959 key = "Inputs"; 960 } 961 else 962 { 963 key = "Outputs"; 964 } 965 output.emplace_back(key, *container); 966 index++; 967 } 968 } 969 970 if (setpointOffset) 971 { 972 // translate between redfish and dbus names 973 if (*setpointOffset == "UpperThresholdNonCritical") 974 { 975 output.emplace_back("SetPointOffset", "WarningLow"); 976 } 977 else if (*setpointOffset == "LowerThresholdNonCritical") 978 { 979 output.emplace_back("SetPointOffset", "WarningHigh"); 980 } 981 else if (*setpointOffset == "LowerThresholdCritical") 982 { 983 output.emplace_back("SetPointOffset", "CriticalLow"); 984 } 985 else if (*setpointOffset == "UpperThresholdCritical") 986 { 987 output.emplace_back("SetPointOffset", "CriticalHigh"); 988 } 989 else 990 { 991 BMCWEB_LOG_ERROR << "Invalid setpointoffset " 992 << *setpointOffset; 993 messages::propertyValueNotInList(response->res, it.key(), 994 "SetPointOffset"); 995 return CreatePIDRet::fail; 996 } 997 } 998 999 // doubles 1000 for (const auto& pairs : doubles) 1001 { 1002 if (!pairs.second) 1003 { 1004 continue; 1005 } 1006 BMCWEB_LOG_DEBUG << pairs.first << " = " << *pairs.second; 1007 output.emplace_back(pairs.first, *pairs.second); 1008 } 1009 } 1010 1011 else if (type == "FanZones") 1012 { 1013 output.emplace_back("Type", "Pid.Zone"); 1014 1015 std::optional<nlohmann::json> chassisContainer; 1016 std::optional<double> failSafePercent; 1017 std::optional<double> minThermalOutput; 1018 if (!redfish::json_util::readJson(it.value(), response->res, "Chassis", 1019 chassisContainer, "FailSafePercent", 1020 failSafePercent, "MinThermalOutput", 1021 minThermalOutput)) 1022 { 1023 BMCWEB_LOG_ERROR 1024 << "Illegal Property " 1025 << it.value().dump(2, ' ', true, 1026 nlohmann::json::error_handler_t::replace); 1027 return CreatePIDRet::fail; 1028 } 1029 1030 if (chassisContainer) 1031 { 1032 1033 std::string chassisId; 1034 if (!redfish::json_util::readJson(*chassisContainer, response->res, 1035 "@odata.id", chassisId)) 1036 { 1037 BMCWEB_LOG_ERROR 1038 << "Illegal Property " 1039 << chassisContainer->dump( 1040 2, ' ', true, 1041 nlohmann::json::error_handler_t::replace); 1042 return CreatePIDRet::fail; 1043 } 1044 1045 // /redfish/v1/chassis/chassis_name/ 1046 if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis)) 1047 { 1048 BMCWEB_LOG_ERROR << "Got invalid path " << chassisId; 1049 messages::invalidObject( 1050 response->res, crow::utility::urlFromPieces( 1051 "redfish", "v1", "Chassis", chassisId)); 1052 return CreatePIDRet::fail; 1053 } 1054 } 1055 if (minThermalOutput) 1056 { 1057 output.emplace_back("MinThermalOutput", *minThermalOutput); 1058 } 1059 if (failSafePercent) 1060 { 1061 output.emplace_back("FailSafePercent", *failSafePercent); 1062 } 1063 } 1064 else if (type == "StepwiseControllers") 1065 { 1066 output.emplace_back("Type", "Stepwise"); 1067 1068 std::optional<std::vector<nlohmann::json>> zones; 1069 std::optional<std::vector<nlohmann::json>> steps; 1070 std::optional<std::vector<std::string>> inputs; 1071 std::optional<double> positiveHysteresis; 1072 std::optional<double> negativeHysteresis; 1073 std::optional<std::string> direction; // upper clipping curve vs lower 1074 if (!redfish::json_util::readJson( 1075 it.value(), response->res, "Zones", zones, "Steps", steps, 1076 "Inputs", inputs, "PositiveHysteresis", positiveHysteresis, 1077 "NegativeHysteresis", negativeHysteresis, "Direction", 1078 direction)) 1079 { 1080 BMCWEB_LOG_ERROR 1081 << "Illegal Property " 1082 << it.value().dump(2, ' ', true, 1083 nlohmann::json::error_handler_t::replace); 1084 return CreatePIDRet::fail; 1085 } 1086 1087 if (zones) 1088 { 1089 std::vector<std::string> zonesStrs; 1090 if (!getZonesFromJsonReq(response, *zones, zonesStrs)) 1091 { 1092 BMCWEB_LOG_ERROR << "Illegal Zones"; 1093 return CreatePIDRet::fail; 1094 } 1095 if (chassis.empty() && 1096 findChassis(managedObj, zonesStrs[0], chassis) == nullptr) 1097 { 1098 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch"; 1099 messages::invalidObject( 1100 response->res, crow::utility::urlFromPieces( 1101 "redfish", "v1", "Chassis", chassis)); 1102 return CreatePIDRet::fail; 1103 } 1104 output.emplace_back("Zones", std::move(zonesStrs)); 1105 } 1106 if (steps) 1107 { 1108 std::vector<double> readings; 1109 std::vector<double> outputs; 1110 for (auto& step : *steps) 1111 { 1112 double target = 0.0; 1113 double out = 0.0; 1114 1115 if (!redfish::json_util::readJson(step, response->res, "Target", 1116 target, "Output", out)) 1117 { 1118 BMCWEB_LOG_ERROR 1119 << "Illegal Property " 1120 << it.value().dump( 1121 2, ' ', true, 1122 nlohmann::json::error_handler_t::replace); 1123 return CreatePIDRet::fail; 1124 } 1125 readings.emplace_back(target); 1126 outputs.emplace_back(out); 1127 } 1128 output.emplace_back("Reading", std::move(readings)); 1129 output.emplace_back("Output", std::move(outputs)); 1130 } 1131 if (inputs) 1132 { 1133 for (std::string& value : *inputs) 1134 { 1135 1136 std::replace(value.begin(), value.end(), '_', ' '); 1137 } 1138 output.emplace_back("Inputs", std::move(*inputs)); 1139 } 1140 if (negativeHysteresis) 1141 { 1142 output.emplace_back("NegativeHysteresis", *negativeHysteresis); 1143 } 1144 if (positiveHysteresis) 1145 { 1146 output.emplace_back("PositiveHysteresis", *positiveHysteresis); 1147 } 1148 if (direction) 1149 { 1150 constexpr const std::array<const char*, 2> allowedDirections = { 1151 "Ceiling", "Floor"}; 1152 if (std::find(allowedDirections.begin(), allowedDirections.end(), 1153 *direction) == allowedDirections.end()) 1154 { 1155 messages::propertyValueTypeError(response->res, "Direction", 1156 *direction); 1157 return CreatePIDRet::fail; 1158 } 1159 output.emplace_back("Class", *direction); 1160 } 1161 } 1162 else 1163 { 1164 BMCWEB_LOG_ERROR << "Illegal Type " << type; 1165 messages::propertyUnknown(response->res, type); 1166 return CreatePIDRet::fail; 1167 } 1168 return CreatePIDRet::patch; 1169 } 1170 struct GetPIDValues : std::enable_shared_from_this<GetPIDValues> 1171 { 1172 1173 explicit GetPIDValues( 1174 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) : 1175 asyncResp(asyncRespIn) 1176 1177 {} 1178 1179 void run() 1180 { 1181 std::shared_ptr<GetPIDValues> self = shared_from_this(); 1182 1183 // get all configurations 1184 crow::connections::systemBus->async_method_call( 1185 [self]( 1186 const boost::system::error_code ec, 1187 const dbus::utility::MapperGetSubTreeResponse& subtreeLocal) { 1188 if (ec) 1189 { 1190 BMCWEB_LOG_ERROR << ec; 1191 messages::internalError(self->asyncResp->res); 1192 return; 1193 } 1194 self->subtree = subtreeLocal; 1195 }, 1196 "xyz.openbmc_project.ObjectMapper", 1197 "/xyz/openbmc_project/object_mapper", 1198 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, 1199 std::array<const char*, 4>{ 1200 pidConfigurationIface, pidZoneConfigurationIface, 1201 objectManagerIface, stepwiseConfigurationIface}); 1202 1203 // at the same time get the selected profile 1204 crow::connections::systemBus->async_method_call( 1205 [self]( 1206 const boost::system::error_code ec, 1207 const dbus::utility::MapperGetSubTreeResponse& subtreeLocal) { 1208 if (ec || subtreeLocal.empty()) 1209 { 1210 return; 1211 } 1212 if (subtreeLocal[0].second.size() != 1) 1213 { 1214 // invalid mapper response, should never happen 1215 BMCWEB_LOG_ERROR << "GetPIDValues: Mapper Error"; 1216 messages::internalError(self->asyncResp->res); 1217 return; 1218 } 1219 1220 const std::string& path = subtreeLocal[0].first; 1221 const std::string& owner = subtreeLocal[0].second[0].first; 1222 crow::connections::systemBus->async_method_call( 1223 [path, owner, 1224 self](const boost::system::error_code ec2, 1225 const dbus::utility::DBusPropertiesMap& resp) { 1226 if (ec2) 1227 { 1228 BMCWEB_LOG_ERROR 1229 << "GetPIDValues: Can't get thermalModeIface " << path; 1230 messages::internalError(self->asyncResp->res); 1231 return; 1232 } 1233 const std::string* current = nullptr; 1234 const std::vector<std::string>* supported = nullptr; 1235 for (const auto& [key, value] : resp) 1236 { 1237 if (key == "Current") 1238 { 1239 current = std::get_if<std::string>(&value); 1240 if (current == nullptr) 1241 { 1242 BMCWEB_LOG_ERROR 1243 << "GetPIDValues: thermal mode iface invalid " 1244 << path; 1245 messages::internalError(self->asyncResp->res); 1246 return; 1247 } 1248 } 1249 if (key == "Supported") 1250 { 1251 supported = 1252 std::get_if<std::vector<std::string>>(&value); 1253 if (supported == nullptr) 1254 { 1255 BMCWEB_LOG_ERROR 1256 << "GetPIDValues: thermal mode iface invalid" 1257 << path; 1258 messages::internalError(self->asyncResp->res); 1259 return; 1260 } 1261 } 1262 } 1263 if (current == nullptr || supported == nullptr) 1264 { 1265 BMCWEB_LOG_ERROR 1266 << "GetPIDValues: thermal mode iface invalid " << path; 1267 messages::internalError(self->asyncResp->res); 1268 return; 1269 } 1270 self->currentProfile = *current; 1271 self->supportedProfiles = *supported; 1272 }, 1273 owner, path, "org.freedesktop.DBus.Properties", "GetAll", 1274 thermalModeIface); 1275 }, 1276 "xyz.openbmc_project.ObjectMapper", 1277 "/xyz/openbmc_project/object_mapper", 1278 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, 1279 std::array<const char*, 1>{thermalModeIface}); 1280 } 1281 1282 ~GetPIDValues() 1283 { 1284 if (asyncResp->res.result() != boost::beast::http::status::ok) 1285 { 1286 return; 1287 } 1288 // create map of <connection, path to objMgr>> 1289 boost::container::flat_map<std::string, std::string> objectMgrPaths; 1290 boost::container::flat_set<std::string> calledConnections; 1291 for (const auto& pathGroup : subtree) 1292 { 1293 for (const auto& connectionGroup : pathGroup.second) 1294 { 1295 auto findConnection = 1296 calledConnections.find(connectionGroup.first); 1297 if (findConnection != calledConnections.end()) 1298 { 1299 break; 1300 } 1301 for (const std::string& interface : connectionGroup.second) 1302 { 1303 if (interface == objectManagerIface) 1304 { 1305 objectMgrPaths[connectionGroup.first] = pathGroup.first; 1306 } 1307 // this list is alphabetical, so we 1308 // should have found the objMgr by now 1309 if (interface == pidConfigurationIface || 1310 interface == pidZoneConfigurationIface || 1311 interface == stepwiseConfigurationIface) 1312 { 1313 auto findObjMgr = 1314 objectMgrPaths.find(connectionGroup.first); 1315 if (findObjMgr == objectMgrPaths.end()) 1316 { 1317 BMCWEB_LOG_DEBUG << connectionGroup.first 1318 << "Has no Object Manager"; 1319 continue; 1320 } 1321 1322 calledConnections.insert(connectionGroup.first); 1323 1324 asyncPopulatePid(findObjMgr->first, findObjMgr->second, 1325 currentProfile, supportedProfiles, 1326 asyncResp); 1327 break; 1328 } 1329 } 1330 } 1331 } 1332 } 1333 1334 GetPIDValues(const GetPIDValues&) = delete; 1335 GetPIDValues(GetPIDValues&&) = delete; 1336 GetPIDValues& operator=(const GetPIDValues&) = delete; 1337 GetPIDValues& operator=(GetPIDValues&&) = delete; 1338 1339 std::vector<std::string> supportedProfiles; 1340 std::string currentProfile; 1341 dbus::utility::MapperGetSubTreeResponse subtree; 1342 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1343 }; 1344 1345 struct SetPIDValues : std::enable_shared_from_this<SetPIDValues> 1346 { 1347 1348 SetPIDValues(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn, 1349 nlohmann::json& data) : 1350 asyncResp(asyncRespIn) 1351 { 1352 1353 std::optional<nlohmann::json> pidControllers; 1354 std::optional<nlohmann::json> fanControllers; 1355 std::optional<nlohmann::json> fanZones; 1356 std::optional<nlohmann::json> stepwiseControllers; 1357 1358 if (!redfish::json_util::readJson( 1359 data, asyncResp->res, "PidControllers", pidControllers, 1360 "FanControllers", fanControllers, "FanZones", fanZones, 1361 "StepwiseControllers", stepwiseControllers, "Profile", profile)) 1362 { 1363 BMCWEB_LOG_ERROR 1364 << "Illegal Property " 1365 << data.dump(2, ' ', true, 1366 nlohmann::json::error_handler_t::replace); 1367 return; 1368 } 1369 configuration.emplace_back("PidControllers", std::move(pidControllers)); 1370 configuration.emplace_back("FanControllers", std::move(fanControllers)); 1371 configuration.emplace_back("FanZones", std::move(fanZones)); 1372 configuration.emplace_back("StepwiseControllers", 1373 std::move(stepwiseControllers)); 1374 } 1375 1376 SetPIDValues(const SetPIDValues&) = delete; 1377 SetPIDValues(SetPIDValues&&) = delete; 1378 SetPIDValues& operator=(const SetPIDValues&) = delete; 1379 SetPIDValues& operator=(SetPIDValues&&) = delete; 1380 1381 void run() 1382 { 1383 if (asyncResp->res.result() != boost::beast::http::status::ok) 1384 { 1385 return; 1386 } 1387 1388 std::shared_ptr<SetPIDValues> self = shared_from_this(); 1389 1390 // todo(james): might make sense to do a mapper call here if this 1391 // interface gets more traction 1392 crow::connections::systemBus->async_method_call( 1393 [self](const boost::system::error_code ec, 1394 const dbus::utility::ManagedObjectType& mObj) { 1395 if (ec) 1396 { 1397 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager"; 1398 messages::internalError(self->asyncResp->res); 1399 return; 1400 } 1401 const std::array<const char*, 3> configurations = { 1402 pidConfigurationIface, pidZoneConfigurationIface, 1403 stepwiseConfigurationIface}; 1404 1405 for (const auto& [path, object] : mObj) 1406 { 1407 for (const auto& [interface, _] : object) 1408 { 1409 if (std::find(configurations.begin(), configurations.end(), 1410 interface) != configurations.end()) 1411 { 1412 self->objectCount++; 1413 break; 1414 } 1415 } 1416 } 1417 self->managedObj = mObj; 1418 }, 1419 "xyz.openbmc_project.EntityManager", "/", objectManagerIface, 1420 "GetManagedObjects"); 1421 1422 // at the same time get the profile information 1423 crow::connections::systemBus->async_method_call( 1424 [self](const boost::system::error_code ec, 1425 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1426 if (ec || subtree.empty()) 1427 { 1428 return; 1429 } 1430 if (subtree[0].second.empty()) 1431 { 1432 // invalid mapper response, should never happen 1433 BMCWEB_LOG_ERROR << "SetPIDValues: Mapper Error"; 1434 messages::internalError(self->asyncResp->res); 1435 return; 1436 } 1437 1438 const std::string& path = subtree[0].first; 1439 const std::string& owner = subtree[0].second[0].first; 1440 crow::connections::systemBus->async_method_call( 1441 [self, path, owner](const boost::system::error_code ec2, 1442 const dbus::utility::DBusPropertiesMap& r) { 1443 if (ec2) 1444 { 1445 BMCWEB_LOG_ERROR 1446 << "SetPIDValues: Can't get thermalModeIface " << path; 1447 messages::internalError(self->asyncResp->res); 1448 return; 1449 } 1450 const std::string* current = nullptr; 1451 const std::vector<std::string>* supported = nullptr; 1452 for (const auto& [key, value] : r) 1453 { 1454 if (key == "Current") 1455 { 1456 current = std::get_if<std::string>(&value); 1457 if (current == nullptr) 1458 { 1459 BMCWEB_LOG_ERROR 1460 << "SetPIDValues: thermal mode iface invalid " 1461 << path; 1462 messages::internalError(self->asyncResp->res); 1463 return; 1464 } 1465 } 1466 if (key == "Supported") 1467 { 1468 supported = 1469 std::get_if<std::vector<std::string>>(&value); 1470 if (supported == nullptr) 1471 { 1472 BMCWEB_LOG_ERROR 1473 << "SetPIDValues: thermal mode iface invalid" 1474 << path; 1475 messages::internalError(self->asyncResp->res); 1476 return; 1477 } 1478 } 1479 } 1480 if (current == nullptr || supported == nullptr) 1481 { 1482 BMCWEB_LOG_ERROR 1483 << "SetPIDValues: thermal mode iface invalid " << path; 1484 messages::internalError(self->asyncResp->res); 1485 return; 1486 } 1487 self->currentProfile = *current; 1488 self->supportedProfiles = *supported; 1489 self->profileConnection = owner; 1490 self->profilePath = path; 1491 }, 1492 owner, path, "org.freedesktop.DBus.Properties", "GetAll", 1493 thermalModeIface); 1494 }, 1495 "xyz.openbmc_project.ObjectMapper", 1496 "/xyz/openbmc_project/object_mapper", 1497 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, 1498 std::array<const char*, 1>{thermalModeIface}); 1499 } 1500 void pidSetDone() 1501 { 1502 if (asyncResp->res.result() != boost::beast::http::status::ok) 1503 { 1504 return; 1505 } 1506 std::shared_ptr<bmcweb::AsyncResp> response = asyncResp; 1507 if (profile) 1508 { 1509 if (std::find(supportedProfiles.begin(), supportedProfiles.end(), 1510 *profile) == supportedProfiles.end()) 1511 { 1512 messages::actionParameterUnknown(response->res, "Profile", 1513 *profile); 1514 return; 1515 } 1516 currentProfile = *profile; 1517 crow::connections::systemBus->async_method_call( 1518 [response](const boost::system::error_code ec) { 1519 if (ec) 1520 { 1521 BMCWEB_LOG_ERROR << "Error patching profile" << ec; 1522 messages::internalError(response->res); 1523 } 1524 }, 1525 profileConnection, profilePath, 1526 "org.freedesktop.DBus.Properties", "Set", thermalModeIface, 1527 "Current", dbus::utility::DbusVariantType(*profile)); 1528 } 1529 1530 for (auto& containerPair : configuration) 1531 { 1532 auto& container = containerPair.second; 1533 if (!container) 1534 { 1535 continue; 1536 } 1537 BMCWEB_LOG_DEBUG << *container; 1538 1539 const std::string& type = containerPair.first; 1540 1541 for (nlohmann::json::iterator it = container->begin(); 1542 it != container->end(); ++it) 1543 { 1544 const auto& name = it.key(); 1545 BMCWEB_LOG_DEBUG << "looking for " << name; 1546 1547 auto pathItr = 1548 std::find_if(managedObj.begin(), managedObj.end(), 1549 [&name](const auto& obj) { 1550 return boost::algorithm::ends_with(obj.first.str, 1551 "/" + name); 1552 }); 1553 dbus::utility::DBusPropertiesMap output; 1554 1555 output.reserve(16); // The pid interface length 1556 1557 // determines if we're patching entity-manager or 1558 // creating a new object 1559 bool createNewObject = (pathItr == managedObj.end()); 1560 BMCWEB_LOG_DEBUG << "Found = " << !createNewObject; 1561 1562 std::string iface; 1563 /* 1564 if (type == "PidControllers" || type == "FanControllers") 1565 { 1566 iface = pidConfigurationIface; 1567 if (!createNewObject && 1568 pathItr->second.find(pidConfigurationIface) == 1569 pathItr->second.end()) 1570 { 1571 createNewObject = true; 1572 } 1573 } 1574 else if (type == "FanZones") 1575 { 1576 iface = pidZoneConfigurationIface; 1577 if (!createNewObject && 1578 pathItr->second.find(pidZoneConfigurationIface) == 1579 pathItr->second.end()) 1580 { 1581 1582 createNewObject = true; 1583 } 1584 } 1585 else if (type == "StepwiseControllers") 1586 { 1587 iface = stepwiseConfigurationIface; 1588 if (!createNewObject && 1589 pathItr->second.find(stepwiseConfigurationIface) == 1590 pathItr->second.end()) 1591 { 1592 createNewObject = true; 1593 } 1594 }*/ 1595 1596 if (createNewObject && it.value() == nullptr) 1597 { 1598 // can't delete a non-existent object 1599 messages::propertyValueNotInList(response->res, 1600 it.value().dump(), name); 1601 continue; 1602 } 1603 1604 std::string path; 1605 if (pathItr != managedObj.end()) 1606 { 1607 path = pathItr->first.str; 1608 } 1609 1610 BMCWEB_LOG_DEBUG << "Create new = " << createNewObject << "\n"; 1611 1612 // arbitrary limit to avoid attacks 1613 constexpr const size_t controllerLimit = 500; 1614 if (createNewObject && objectCount >= controllerLimit) 1615 { 1616 messages::resourceExhaustion(response->res, type); 1617 continue; 1618 } 1619 std::string escaped = name; 1620 std::replace(escaped.begin(), escaped.end(), '_', ' '); 1621 output.emplace_back("Name", escaped); 1622 1623 std::string chassis; 1624 CreatePIDRet ret = createPidInterface( 1625 response, type, it, path, managedObj, createNewObject, 1626 output, chassis, currentProfile); 1627 if (ret == CreatePIDRet::fail) 1628 { 1629 return; 1630 } 1631 if (ret == CreatePIDRet::del) 1632 { 1633 continue; 1634 } 1635 1636 if (!createNewObject) 1637 { 1638 for (const auto& property : output) 1639 { 1640 crow::connections::systemBus->async_method_call( 1641 [response, 1642 propertyName{std::string(property.first)}]( 1643 const boost::system::error_code ec) { 1644 if (ec) 1645 { 1646 BMCWEB_LOG_ERROR << "Error patching " 1647 << propertyName << ": " << ec; 1648 messages::internalError(response->res); 1649 return; 1650 } 1651 messages::success(response->res); 1652 }, 1653 "xyz.openbmc_project.EntityManager", path, 1654 "org.freedesktop.DBus.Properties", "Set", iface, 1655 property.first, property.second); 1656 } 1657 } 1658 else 1659 { 1660 if (chassis.empty()) 1661 { 1662 BMCWEB_LOG_ERROR << "Failed to get chassis from config"; 1663 messages::internalError(response->res); 1664 return; 1665 } 1666 1667 bool foundChassis = false; 1668 for (const auto& obj : managedObj) 1669 { 1670 if (boost::algorithm::ends_with(obj.first.str, chassis)) 1671 { 1672 chassis = obj.first.str; 1673 foundChassis = true; 1674 break; 1675 } 1676 } 1677 if (!foundChassis) 1678 { 1679 BMCWEB_LOG_ERROR << "Failed to find chassis on dbus"; 1680 messages::resourceMissingAtURI( 1681 response->res, 1682 crow::utility::urlFromPieces("redfish", "v1", 1683 "Chassis", chassis)); 1684 return; 1685 } 1686 1687 crow::connections::systemBus->async_method_call( 1688 [response](const boost::system::error_code ec) { 1689 if (ec) 1690 { 1691 BMCWEB_LOG_ERROR << "Error Adding Pid Object " 1692 << ec; 1693 messages::internalError(response->res); 1694 return; 1695 } 1696 messages::success(response->res); 1697 }, 1698 "xyz.openbmc_project.EntityManager", chassis, 1699 "xyz.openbmc_project.AddObject", "AddObject", output); 1700 } 1701 } 1702 } 1703 } 1704 1705 ~SetPIDValues() 1706 { 1707 try 1708 { 1709 pidSetDone(); 1710 } 1711 catch (...) 1712 { 1713 BMCWEB_LOG_CRITICAL << "pidSetDone threw exception"; 1714 } 1715 } 1716 1717 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1718 std::vector<std::pair<std::string, std::optional<nlohmann::json>>> 1719 configuration; 1720 std::optional<std::string> profile; 1721 dbus::utility::ManagedObjectType managedObj; 1722 std::vector<std::string> supportedProfiles; 1723 std::string currentProfile; 1724 std::string profileConnection; 1725 std::string profilePath; 1726 size_t objectCount = 0; 1727 }; 1728 1729 /** 1730 * @brief Retrieves BMC manager location data over DBus 1731 * 1732 * @param[in] aResp Shared pointer for completing asynchronous calls 1733 * @param[in] connectionName - service name 1734 * @param[in] path - object path 1735 * @return none 1736 */ 1737 inline void getLocation(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1738 const std::string& connectionName, 1739 const std::string& path) 1740 { 1741 BMCWEB_LOG_DEBUG << "Get BMC manager Location data."; 1742 1743 sdbusplus::asio::getProperty<std::string>( 1744 *crow::connections::systemBus, connectionName, path, 1745 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 1746 [aResp](const boost::system::error_code ec, 1747 const std::string& property) { 1748 if (ec) 1749 { 1750 BMCWEB_LOG_DEBUG << "DBUS response error for " 1751 "Location"; 1752 messages::internalError(aResp->res); 1753 return; 1754 } 1755 1756 aResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 1757 property; 1758 }); 1759 } 1760 // avoid name collision systems.hpp 1761 inline void 1762 managerGetLastResetTime(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1763 { 1764 BMCWEB_LOG_DEBUG << "Getting Manager Last Reset Time"; 1765 1766 sdbusplus::asio::getProperty<uint64_t>( 1767 *crow::connections::systemBus, "xyz.openbmc_project.State.BMC", 1768 "/xyz/openbmc_project/state/bmc0", "xyz.openbmc_project.State.BMC", 1769 "LastRebootTime", 1770 [aResp](const boost::system::error_code ec, 1771 const uint64_t lastResetTime) { 1772 if (ec) 1773 { 1774 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec; 1775 return; 1776 } 1777 1778 // LastRebootTime is epoch time, in milliseconds 1779 // https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19 1780 uint64_t lastResetTimeStamp = lastResetTime / 1000; 1781 1782 // Convert to ISO 8601 standard 1783 aResp->res.jsonValue["LastResetTime"] = 1784 crow::utility::getDateTimeUint(lastResetTimeStamp); 1785 }); 1786 } 1787 1788 /** 1789 * @brief Set the running firmware image 1790 * 1791 * @param[i,o] aResp - Async response object 1792 * @param[i] runningFirmwareTarget - Image to make the running image 1793 * 1794 * @return void 1795 */ 1796 inline void 1797 setActiveFirmwareImage(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1798 const std::string& runningFirmwareTarget) 1799 { 1800 // Get the Id from /redfish/v1/UpdateService/FirmwareInventory/<Id> 1801 std::string::size_type idPos = runningFirmwareTarget.rfind('/'); 1802 if (idPos == std::string::npos) 1803 { 1804 messages::propertyValueNotInList(aResp->res, runningFirmwareTarget, 1805 "@odata.id"); 1806 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!"; 1807 return; 1808 } 1809 idPos++; 1810 if (idPos >= runningFirmwareTarget.size()) 1811 { 1812 messages::propertyValueNotInList(aResp->res, runningFirmwareTarget, 1813 "@odata.id"); 1814 BMCWEB_LOG_DEBUG << "Invalid firmware ID."; 1815 return; 1816 } 1817 std::string firmwareId = runningFirmwareTarget.substr(idPos); 1818 1819 // Make sure the image is valid before setting priority 1820 crow::connections::systemBus->async_method_call( 1821 [aResp, firmwareId, 1822 runningFirmwareTarget](const boost::system::error_code ec, 1823 dbus::utility::ManagedObjectType& subtree) { 1824 if (ec) 1825 { 1826 BMCWEB_LOG_DEBUG << "D-Bus response error getting objects."; 1827 messages::internalError(aResp->res); 1828 return; 1829 } 1830 1831 if (subtree.empty()) 1832 { 1833 BMCWEB_LOG_DEBUG << "Can't find image!"; 1834 messages::internalError(aResp->res); 1835 return; 1836 } 1837 1838 bool foundImage = false; 1839 for (const auto& object : subtree) 1840 { 1841 const std::string& path = 1842 static_cast<const std::string&>(object.first); 1843 std::size_t idPos2 = path.rfind('/'); 1844 1845 if (idPos2 == std::string::npos) 1846 { 1847 continue; 1848 } 1849 1850 idPos2++; 1851 if (idPos2 >= path.size()) 1852 { 1853 continue; 1854 } 1855 1856 if (path.substr(idPos2) == firmwareId) 1857 { 1858 foundImage = true; 1859 break; 1860 } 1861 } 1862 1863 if (!foundImage) 1864 { 1865 messages::propertyValueNotInList(aResp->res, runningFirmwareTarget, 1866 "@odata.id"); 1867 BMCWEB_LOG_DEBUG << "Invalid firmware ID."; 1868 return; 1869 } 1870 1871 BMCWEB_LOG_DEBUG << "Setting firmware version " << firmwareId 1872 << " to priority 0."; 1873 1874 // Only support Immediate 1875 // An addition could be a Redfish Setting like 1876 // ActiveSoftwareImageApplyTime and support OnReset 1877 crow::connections::systemBus->async_method_call( 1878 [aResp](const boost::system::error_code ec2) { 1879 if (ec2) 1880 { 1881 BMCWEB_LOG_DEBUG << "D-Bus response error setting."; 1882 messages::internalError(aResp->res); 1883 return; 1884 } 1885 doBMCGracefulRestart(aResp); 1886 }, 1887 1888 "xyz.openbmc_project.Software.BMC.Updater", 1889 "/xyz/openbmc_project/software/" + firmwareId, 1890 "org.freedesktop.DBus.Properties", "Set", 1891 "xyz.openbmc_project.Software.RedundancyPriority", "Priority", 1892 dbus::utility::DbusVariantType(static_cast<uint8_t>(0))); 1893 }, 1894 "xyz.openbmc_project.Software.BMC.Updater", 1895 "/xyz/openbmc_project/software", "org.freedesktop.DBus.ObjectManager", 1896 "GetManagedObjects"); 1897 } 1898 1899 inline void setDateTime(std::shared_ptr<bmcweb::AsyncResp> aResp, 1900 std::string datetime) 1901 { 1902 BMCWEB_LOG_DEBUG << "Set date time: " << datetime; 1903 1904 std::stringstream stream(datetime); 1905 // Convert from ISO 8601 to boost local_time 1906 // (BMC only has time in UTC) 1907 boost::posix_time::ptime posixTime; 1908 boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1)); 1909 // Facet gets deleted with the stringsteam 1910 auto ifc = std::make_unique<boost::local_time::local_time_input_facet>( 1911 "%Y-%m-%d %H:%M:%S%F %ZP"); 1912 stream.imbue(std::locale(stream.getloc(), ifc.release())); 1913 1914 boost::local_time::local_date_time ldt(boost::local_time::not_a_date_time); 1915 1916 if (stream >> ldt) 1917 { 1918 posixTime = ldt.utc_time(); 1919 boost::posix_time::time_duration dur = posixTime - epoch; 1920 uint64_t durMicroSecs = static_cast<uint64_t>(dur.total_microseconds()); 1921 crow::connections::systemBus->async_method_call( 1922 [aResp{std::move(aResp)}, datetime{std::move(datetime)}]( 1923 const boost::system::error_code ec) { 1924 if (ec) 1925 { 1926 BMCWEB_LOG_DEBUG << "Failed to set elapsed time. " 1927 "DBUS response error " 1928 << ec; 1929 messages::internalError(aResp->res); 1930 return; 1931 } 1932 aResp->res.jsonValue["DateTime"] = datetime; 1933 }, 1934 "xyz.openbmc_project.Time.Manager", "/xyz/openbmc_project/time/bmc", 1935 "org.freedesktop.DBus.Properties", "Set", 1936 "xyz.openbmc_project.Time.EpochTime", "Elapsed", 1937 dbus::utility::DbusVariantType(durMicroSecs)); 1938 } 1939 else 1940 { 1941 messages::propertyValueFormatError(aResp->res, datetime, "DateTime"); 1942 return; 1943 } 1944 } 1945 1946 inline void requestRoutesManager(App& app) 1947 { 1948 std::string uuid = persistent_data::getConfig().systemUuid; 1949 1950 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/") 1951 .privileges(redfish::privileges::getManager) 1952 .methods(boost::beast::http::verb::get)( 1953 [&app, uuid](const crow::Request& req, 1954 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1955 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1956 { 1957 return; 1958 } 1959 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc"; 1960 asyncResp->res.jsonValue["@odata.type"] = "#Manager.v1_14_0.Manager"; 1961 asyncResp->res.jsonValue["Id"] = "bmc"; 1962 asyncResp->res.jsonValue["Name"] = "OpenBmc Manager"; 1963 asyncResp->res.jsonValue["Description"] = 1964 "Baseboard Management Controller"; 1965 asyncResp->res.jsonValue["PowerState"] = "On"; 1966 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 1967 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 1968 1969 asyncResp->res.jsonValue["ManagerType"] = "BMC"; 1970 asyncResp->res.jsonValue["UUID"] = systemd_utils::getUuid(); 1971 asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid; 1972 asyncResp->res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model 1973 1974 asyncResp->res.jsonValue["LogServices"]["@odata.id"] = 1975 "/redfish/v1/Managers/bmc/LogServices"; 1976 asyncResp->res.jsonValue["NetworkProtocol"]["@odata.id"] = 1977 "/redfish/v1/Managers/bmc/NetworkProtocol"; 1978 asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = 1979 "/redfish/v1/Managers/bmc/EthernetInterfaces"; 1980 1981 #ifdef BMCWEB_ENABLE_VM_NBDPROXY 1982 asyncResp->res.jsonValue["VirtualMedia"]["@odata.id"] = 1983 "/redfish/v1/Managers/bmc/VirtualMedia"; 1984 #endif // BMCWEB_ENABLE_VM_NBDPROXY 1985 1986 // default oem data 1987 nlohmann::json& oem = asyncResp->res.jsonValue["Oem"]; 1988 nlohmann::json& oemOpenbmc = oem["OpenBmc"]; 1989 oem["@odata.type"] = "#OemManager.Oem"; 1990 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem"; 1991 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc"; 1992 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc"; 1993 1994 nlohmann::json::object_t certificates; 1995 certificates["@odata.id"] = 1996 "/redfish/v1/Managers/bmc/Truststore/Certificates"; 1997 oemOpenbmc["Certificates"] = std::move(certificates); 1998 1999 // Manager.Reset (an action) can be many values, OpenBMC only 2000 // supports BMC reboot. 2001 nlohmann::json& managerReset = 2002 asyncResp->res.jsonValue["Actions"]["#Manager.Reset"]; 2003 managerReset["target"] = 2004 "/redfish/v1/Managers/bmc/Actions/Manager.Reset"; 2005 managerReset["@Redfish.ActionInfo"] = 2006 "/redfish/v1/Managers/bmc/ResetActionInfo"; 2007 2008 // ResetToDefaults (Factory Reset) has values like 2009 // PreserveNetworkAndUsers and PreserveNetwork that aren't supported 2010 // on OpenBMC 2011 nlohmann::json& resetToDefaults = 2012 asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"]; 2013 resetToDefaults["target"] = 2014 "/redfish/v1/Managers/bmc/Actions/Manager.ResetToDefaults"; 2015 resetToDefaults["ResetType@Redfish.AllowableValues"] = {"ResetAll"}; 2016 2017 std::pair<std::string, std::string> redfishDateTimeOffset = 2018 crow::utility::getDateTimeOffsetNow(); 2019 2020 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 2021 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 2022 redfishDateTimeOffset.second; 2023 2024 // TODO (Gunnar): Remove these one day since moved to ComputerSystem 2025 // Still used by OCP profiles 2026 // https://github.com/opencomputeproject/OCP-Profiles/issues/23 2027 // Fill in SerialConsole info 2028 asyncResp->res.jsonValue["SerialConsole"]["ServiceEnabled"] = true; 2029 asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15; 2030 asyncResp->res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = { 2031 "IPMI", "SSH"}; 2032 #ifdef BMCWEB_ENABLE_KVM 2033 // Fill in GraphicalConsole info 2034 asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true; 2035 asyncResp->res.jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 2036 4; 2037 asyncResp->res 2038 .jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = {"KVMIP"}; 2039 #endif // BMCWEB_ENABLE_KVM 2040 2041 asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1; 2042 2043 nlohmann::json::array_t managerForServers; 2044 nlohmann::json::object_t manager; 2045 manager["@odata.id"] = "/redfish/v1/Systems/system"; 2046 managerForServers.push_back(std::move(manager)); 2047 2048 asyncResp->res.jsonValue["Links"]["ManagerForServers"] = 2049 std::move(managerForServers); 2050 2051 auto health = std::make_shared<HealthPopulate>(asyncResp); 2052 health->isManagersHealth = true; 2053 health->populate(); 2054 2055 sw_util::populateSoftwareInformation(asyncResp, sw_util::bmcPurpose, 2056 "FirmwareVersion", true); 2057 2058 managerGetLastResetTime(asyncResp); 2059 2060 // ManagerDiagnosticData is added for all BMCs. 2061 nlohmann::json& managerDiagnosticData = 2062 asyncResp->res.jsonValue["ManagerDiagnosticData"]; 2063 managerDiagnosticData["@odata.id"] = 2064 "/redfish/v1/Managers/bmc/ManagerDiagnosticData"; 2065 2066 auto pids = std::make_shared<GetPIDValues>(asyncResp); 2067 pids->run(); 2068 2069 getMainChassisId(asyncResp, 2070 [](const std::string& chassisId, 2071 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { 2072 aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1; 2073 nlohmann::json::array_t managerForChassis; 2074 nlohmann::json::object_t managerObj; 2075 managerObj["@odata.id"] = "/redfish/v1/Chassis/" + chassisId; 2076 managerForChassis.push_back(std::move(managerObj)); 2077 aRsp->res.jsonValue["Links"]["ManagerForChassis"] = 2078 std::move(managerForChassis); 2079 aRsp->res.jsonValue["Links"]["ManagerInChassis"]["@odata.id"] = 2080 "/redfish/v1/Chassis/" + chassisId; 2081 }); 2082 2083 static bool started = false; 2084 2085 if (!started) 2086 { 2087 sdbusplus::asio::getProperty<double>( 2088 *crow::connections::systemBus, "org.freedesktop.systemd1", 2089 "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", 2090 "Progress", 2091 [asyncResp](const boost::system::error_code ec, 2092 const double& val) { 2093 if (ec) 2094 { 2095 BMCWEB_LOG_ERROR << "Error while getting progress"; 2096 messages::internalError(asyncResp->res); 2097 return; 2098 } 2099 if (val < 1.0) 2100 { 2101 asyncResp->res.jsonValue["Status"]["State"] = "Starting"; 2102 started = true; 2103 } 2104 }); 2105 } 2106 2107 crow::connections::systemBus->async_method_call( 2108 [asyncResp]( 2109 const boost::system::error_code ec, 2110 const dbus::utility::MapperGetSubTreeResponse& subtree) { 2111 if (ec) 2112 { 2113 BMCWEB_LOG_DEBUG << "D-Bus response error on GetSubTree " << ec; 2114 return; 2115 } 2116 if (subtree.empty()) 2117 { 2118 BMCWEB_LOG_DEBUG << "Can't find bmc D-Bus object!"; 2119 return; 2120 } 2121 // Assume only 1 bmc D-Bus object 2122 // Throw an error if there is more than 1 2123 if (subtree.size() > 1) 2124 { 2125 BMCWEB_LOG_DEBUG << "Found more than 1 bmc D-Bus object!"; 2126 messages::internalError(asyncResp->res); 2127 return; 2128 } 2129 2130 if (subtree[0].first.empty() || subtree[0].second.size() != 1) 2131 { 2132 BMCWEB_LOG_DEBUG << "Error getting bmc D-Bus object!"; 2133 messages::internalError(asyncResp->res); 2134 return; 2135 } 2136 2137 const std::string& path = subtree[0].first; 2138 const std::string& connectionName = subtree[0].second[0].first; 2139 2140 for (const auto& interfaceName : subtree[0].second[0].second) 2141 { 2142 if (interfaceName == 2143 "xyz.openbmc_project.Inventory.Decorator.Asset") 2144 { 2145 crow::connections::systemBus->async_method_call( 2146 [asyncResp](const boost::system::error_code ec2, 2147 const dbus::utility::DBusPropertiesMap& 2148 propertiesList) { 2149 if (ec2) 2150 { 2151 BMCWEB_LOG_DEBUG << "Can't get bmc asset!"; 2152 return; 2153 } 2154 for (const std::pair<std::string, 2155 dbus::utility::DbusVariantType>& 2156 property : propertiesList) 2157 { 2158 const std::string& propertyName = property.first; 2159 2160 if ((propertyName == "PartNumber") || 2161 (propertyName == "SerialNumber") || 2162 (propertyName == "Manufacturer") || 2163 (propertyName == "Model") || 2164 (propertyName == "SparePartNumber")) 2165 { 2166 const std::string* value = 2167 std::get_if<std::string>(&property.second); 2168 if (value == nullptr) 2169 { 2170 // illegal property 2171 messages::internalError(asyncResp->res); 2172 return; 2173 } 2174 asyncResp->res.jsonValue[propertyName] = *value; 2175 } 2176 } 2177 }, 2178 connectionName, path, "org.freedesktop.DBus.Properties", 2179 "GetAll", 2180 "xyz.openbmc_project.Inventory.Decorator.Asset"); 2181 } 2182 else if (interfaceName == 2183 "xyz.openbmc_project.Inventory.Decorator.LocationCode") 2184 { 2185 getLocation(asyncResp, connectionName, path); 2186 } 2187 } 2188 }, 2189 "xyz.openbmc_project.ObjectMapper", 2190 "/xyz/openbmc_project/object_mapper", 2191 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 2192 "/xyz/openbmc_project/inventory", int32_t(0), 2193 std::array<const char*, 1>{ 2194 "xyz.openbmc_project.Inventory.Item.Bmc"}); 2195 }); 2196 2197 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/") 2198 .privileges(redfish::privileges::patchManager) 2199 .methods(boost::beast::http::verb::patch)( 2200 [&app](const crow::Request& req, 2201 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2202 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2203 { 2204 return; 2205 } 2206 std::optional<nlohmann::json> oem; 2207 std::optional<nlohmann::json> links; 2208 std::optional<std::string> datetime; 2209 2210 if (!json_util::readJsonPatch(req, asyncResp->res, "Oem", oem, 2211 "DateTime", datetime, "Links", links)) 2212 { 2213 return; 2214 } 2215 2216 if (oem) 2217 { 2218 std::optional<nlohmann::json> openbmc; 2219 if (!redfish::json_util::readJson(*oem, asyncResp->res, "OpenBmc", 2220 openbmc)) 2221 { 2222 BMCWEB_LOG_ERROR 2223 << "Illegal Property " 2224 << oem->dump(2, ' ', true, 2225 nlohmann::json::error_handler_t::replace); 2226 return; 2227 } 2228 if (openbmc) 2229 { 2230 std::optional<nlohmann::json> fan; 2231 if (!redfish::json_util::readJson(*openbmc, asyncResp->res, 2232 "Fan", fan)) 2233 { 2234 BMCWEB_LOG_ERROR 2235 << "Illegal Property " 2236 << openbmc->dump( 2237 2, ' ', true, 2238 nlohmann::json::error_handler_t::replace); 2239 return; 2240 } 2241 if (fan) 2242 { 2243 auto pid = std::make_shared<SetPIDValues>(asyncResp, *fan); 2244 pid->run(); 2245 } 2246 } 2247 } 2248 if (links) 2249 { 2250 std::optional<nlohmann::json> activeSoftwareImage; 2251 if (!redfish::json_util::readJson(*links, asyncResp->res, 2252 "ActiveSoftwareImage", 2253 activeSoftwareImage)) 2254 { 2255 return; 2256 } 2257 if (activeSoftwareImage) 2258 { 2259 std::optional<std::string> odataId; 2260 if (!json_util::readJson(*activeSoftwareImage, asyncResp->res, 2261 "@odata.id", odataId)) 2262 { 2263 return; 2264 } 2265 2266 if (odataId) 2267 { 2268 setActiveFirmwareImage(asyncResp, *odataId); 2269 } 2270 } 2271 } 2272 if (datetime) 2273 { 2274 setDateTime(asyncResp, std::move(*datetime)); 2275 } 2276 }); 2277 } 2278 2279 inline void requestRoutesManagerCollection(App& app) 2280 { 2281 BMCWEB_ROUTE(app, "/redfish/v1/Managers/") 2282 .privileges(redfish::privileges::getManagerCollection) 2283 .methods(boost::beast::http::verb::get)( 2284 [&app](const crow::Request& req, 2285 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2286 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2287 { 2288 return; 2289 } 2290 // Collections don't include the static data added by SubRoute 2291 // because it has a duplicate entry for members 2292 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers"; 2293 asyncResp->res.jsonValue["@odata.type"] = 2294 "#ManagerCollection.ManagerCollection"; 2295 asyncResp->res.jsonValue["Name"] = "Manager Collection"; 2296 asyncResp->res.jsonValue["Members@odata.count"] = 1; 2297 nlohmann::json::array_t members; 2298 nlohmann::json& bmc = members.emplace_back(); 2299 bmc["@odata.id"] = "/redfish/v1/Managers/bmc"; 2300 asyncResp->res.jsonValue["Members"] = std::move(members); 2301 }); 2302 } 2303 } // namespace redfish 2304