1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation 4 #pragma once 5 6 #include "bmcweb_config.h" 7 8 #include "app.hpp" 9 #include "async_resp.hpp" 10 #include "dbus_singleton.hpp" 11 #include "dbus_utility.hpp" 12 #include "error_messages.hpp" 13 #include "generated/enums/action_info.hpp" 14 #include "generated/enums/manager.hpp" 15 #include "generated/enums/resource.hpp" 16 #include "http_request.hpp" 17 #include "led.hpp" 18 #include "logging.hpp" 19 #include "persistent_data.hpp" 20 #include "query.hpp" 21 #include "redfish.hpp" 22 #include "redfish_util.hpp" 23 #include "registries/privilege_registry.hpp" 24 #include "utils/dbus_utils.hpp" 25 #include "utils/json_utils.hpp" 26 #include "utils/manager_utils.hpp" 27 #include "utils/sw_utils.hpp" 28 #include "utils/systemd_utils.hpp" 29 #include "utils/time_utils.hpp" 30 31 #include <systemd/sd-bus.h> 32 33 #include <boost/beast/http/status.hpp> 34 #include <boost/beast/http/verb.hpp> 35 #include <boost/system/error_code.hpp> 36 #include <boost/url/format.hpp> 37 #include <boost/url/url.hpp> 38 #include <nlohmann/json.hpp> 39 #include <sdbusplus/asio/property.hpp> 40 #include <sdbusplus/message.hpp> 41 #include <sdbusplus/message/native_types.hpp> 42 #include <sdbusplus/unpack_properties.hpp> 43 44 #include <array> 45 #include <cstddef> 46 #include <cstdint> 47 #include <format> 48 #include <functional> 49 #include <map> 50 #include <memory> 51 #include <optional> 52 #include <ranges> 53 #include <string> 54 #include <string_view> 55 #include <utility> 56 #include <vector> 57 58 namespace redfish 59 { 60 61 inline void handleSetLocationIndicatorActive( 62 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 63 bool locationIndicatorActive, const std::string& managerId, 64 const boost::system::error_code& ec, 65 const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) 66 { 67 if (ec) 68 { 69 if (ec == boost::system::errc::io_error) 70 { 71 // Not found 72 BMCWEB_LOG_WARNING("Manager {} not found", managerId); 73 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 74 return; 75 } 76 BMCWEB_LOG_ERROR("D-Bus response error {}", ec.value()); 77 messages::internalError(asyncResp->res); 78 return; 79 } 80 if (subtreePaths.empty()) 81 { 82 BMCWEB_LOG_WARNING("Manager {} not found", managerId); 83 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 84 return; 85 } 86 // Assume only 1 bmc D-Bus object 87 // Throw an error if there is more than 1 88 if (subtreePaths.size() != 1) 89 { 90 BMCWEB_LOG_ERROR("Found {} Bmc D-Bus paths", subtreePaths.size()); 91 messages::internalError(asyncResp->res); 92 return; 93 } 94 95 setLocationIndicatorActive(asyncResp, subtreePaths[0], 96 locationIndicatorActive); 97 } 98 99 /** 100 * Set the locationIndicatorActive. 101 * 102 * @param[in,out] asyncResp Async HTTP response. 103 * @param[in] locationIndicatorActive Value of the property 104 */ 105 inline void setLocationIndicatorActiveState( 106 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 107 bool locationIndicatorActive, const std::string& managerId) 108 { 109 // GetSubTree on all interfaces which provide info about a Manager 110 constexpr std::array<std::string_view, 1> interfaces = { 111 "xyz.openbmc_project.Inventory.Item.Bmc"}; 112 dbus::utility::getSubTreePaths( 113 "/xyz/openbmc_project/inventory", 0, interfaces, 114 std::bind_front(handleSetLocationIndicatorActive, asyncResp, 115 locationIndicatorActive, managerId)); 116 } 117 118 inline std::string getBMCUpdateServiceName() 119 { 120 if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 121 { 122 return "xyz.openbmc_project.Software.Manager"; 123 } 124 return "xyz.openbmc_project.Software.BMC.Updater"; 125 } 126 127 inline std::string getBMCUpdateServicePath() 128 { 129 if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 130 { 131 return "/xyz/openbmc_project/software/bmc"; 132 } 133 return "/xyz/openbmc_project/software"; 134 } 135 136 /** 137 * Function reboots the BMC. 138 * 139 * @param[in] asyncResp - Shared pointer for completing asynchronous calls 140 */ 141 inline void doBMCGracefulRestart( 142 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 143 { 144 const char* processName = "xyz.openbmc_project.State.BMC"; 145 const char* objectPath = "/xyz/openbmc_project/state/bmc0"; 146 const char* interfaceName = "xyz.openbmc_project.State.BMC"; 147 const std::string& propertyValue = 148 "xyz.openbmc_project.State.BMC.Transition.Reboot"; 149 const char* destProperty = "RequestedBMCTransition"; 150 151 // Create the D-Bus variant for D-Bus call. 152 sdbusplus::asio::setProperty( 153 *crow::connections::systemBus, processName, objectPath, interfaceName, 154 destProperty, propertyValue, 155 [asyncResp](const boost::system::error_code& ec) { 156 // Use "Set" method to set the property value. 157 if (ec) 158 { 159 BMCWEB_LOG_DEBUG("[Set] Bad D-Bus request error: {}", ec); 160 messages::internalError(asyncResp->res); 161 return; 162 } 163 164 messages::success(asyncResp->res); 165 }); 166 } 167 168 inline void doBMCForceRestart( 169 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 170 { 171 const char* processName = "xyz.openbmc_project.State.BMC"; 172 const char* objectPath = "/xyz/openbmc_project/state/bmc0"; 173 const char* interfaceName = "xyz.openbmc_project.State.BMC"; 174 const std::string& propertyValue = 175 "xyz.openbmc_project.State.BMC.Transition.HardReboot"; 176 const char* destProperty = "RequestedBMCTransition"; 177 178 // Create the D-Bus variant for D-Bus call. 179 sdbusplus::asio::setProperty( 180 *crow::connections::systemBus, processName, objectPath, interfaceName, 181 destProperty, propertyValue, 182 [asyncResp](const boost::system::error_code& ec) { 183 // Use "Set" method to set the property value. 184 if (ec) 185 { 186 BMCWEB_LOG_DEBUG("[Set] Bad D-Bus request error: {}", ec); 187 messages::internalError(asyncResp->res); 188 return; 189 } 190 191 messages::success(asyncResp->res); 192 }); 193 } 194 195 /** 196 * ManagerResetAction class supports the POST method for the Reset (reboot) 197 * action. 198 */ 199 inline void requestRoutesManagerResetAction(App& app) 200 { 201 /** 202 * Function handles POST method request. 203 * Analyzes POST body before sending Reset (Reboot) request data to D-Bus. 204 * OpenBMC supports ResetType "GracefulRestart" and "ForceRestart". 205 */ 206 207 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/Actions/Manager.Reset/") 208 .privileges(redfish::privileges::postManager) 209 .methods(boost::beast::http::verb::post)( 210 [&app](const crow::Request& req, 211 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 212 const std::string& managerId) { 213 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 214 { 215 return; 216 } 217 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 218 { 219 messages::resourceNotFound(asyncResp->res, "Manager", 220 managerId); 221 return; 222 } 223 224 BMCWEB_LOG_DEBUG("Post Manager Reset."); 225 226 std::string resetType; 227 228 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", 229 resetType)) 230 { 231 return; 232 } 233 234 if (resetType == "GracefulRestart") 235 { 236 BMCWEB_LOG_DEBUG("Proceeding with {}", resetType); 237 doBMCGracefulRestart(asyncResp); 238 return; 239 } 240 if (resetType == "ForceRestart") 241 { 242 BMCWEB_LOG_DEBUG("Proceeding with {}", resetType); 243 doBMCForceRestart(asyncResp); 244 return; 245 } 246 BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", 247 resetType); 248 messages::actionParameterNotSupported(asyncResp->res, resetType, 249 "ResetType"); 250 251 return; 252 }); 253 } 254 255 /** 256 * ManagerResetToDefaultsAction class supports POST method for factory reset 257 * action. 258 */ 259 inline void requestRoutesManagerResetToDefaultsAction(App& app) 260 { 261 /** 262 * Function handles ResetToDefaults POST method request. 263 * 264 * Analyzes POST body message and factory resets BMC by calling 265 * BMC code updater factory reset followed by a BMC reboot. 266 * 267 * BMC code updater factory reset wipes the whole BMC read-write 268 * filesystem which includes things like the network settings. 269 * 270 * OpenBMC only supports ResetType "ResetAll". 271 */ 272 273 BMCWEB_ROUTE(app, 274 "/redfish/v1/Managers/<str>/Actions/Manager.ResetToDefaults/") 275 .privileges(redfish::privileges::postManager) 276 .methods(boost::beast::http::verb::post)( 277 [&app](const crow::Request& req, 278 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 279 const std::string& managerId) { 280 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 281 { 282 return; 283 } 284 285 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 286 { 287 messages::resourceNotFound(asyncResp->res, "Manager", 288 managerId); 289 return; 290 } 291 292 BMCWEB_LOG_DEBUG("Post ResetToDefaults."); 293 294 std::optional<std::string> resetType; 295 296 if (!json_util::readJsonAction( // 297 req, asyncResp->res, // 298 "ResetType", resetType // 299 )) 300 { 301 BMCWEB_LOG_DEBUG("Missing property ResetType."); 302 303 messages::actionParameterMissing( 304 asyncResp->res, "ResetToDefaults", "ResetType"); 305 return; 306 } 307 308 if (resetType != "ResetAll") 309 { 310 BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", 311 *resetType); 312 messages::actionParameterNotSupported( 313 asyncResp->res, *resetType, "ResetType"); 314 return; 315 } 316 317 crow::connections::systemBus->async_method_call( 318 [asyncResp](const boost::system::error_code& ec) { 319 if (ec) 320 { 321 BMCWEB_LOG_DEBUG("Failed to ResetToDefaults: {}", 322 ec); 323 messages::internalError(asyncResp->res); 324 return; 325 } 326 // Factory Reset doesn't actually happen until a reboot 327 // Can't erase what the BMC is running on 328 doBMCGracefulRestart(asyncResp); 329 }, 330 getBMCUpdateServiceName(), getBMCUpdateServicePath(), 331 "xyz.openbmc_project.Common.FactoryReset", "Reset"); 332 }); 333 } 334 335 /** 336 * ManagerResetActionInfo derived class for delivering Manager 337 * ResetType AllowableValues using ResetInfo schema. 338 */ 339 inline void requestRoutesManagerResetActionInfo(App& app) 340 { 341 /** 342 * Functions triggers appropriate requests on DBus 343 */ 344 345 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/ResetActionInfo/") 346 .privileges(redfish::privileges::getActionInfo) 347 .methods(boost::beast::http::verb::get)( 348 [&app](const crow::Request& req, 349 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 350 const std::string& managerId) { 351 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 352 { 353 return; 354 } 355 356 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 357 { 358 messages::resourceNotFound(asyncResp->res, "Manager", 359 managerId); 360 return; 361 } 362 363 asyncResp->res.jsonValue["@odata.type"] = 364 "#ActionInfo.v1_1_2.ActionInfo"; 365 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 366 "/redfish/v1/Managers/{}/ResetActionInfo", 367 BMCWEB_REDFISH_MANAGER_URI_NAME); 368 asyncResp->res.jsonValue["Name"] = "Reset Action Info"; 369 asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; 370 nlohmann::json::object_t parameter; 371 parameter["Name"] = "ResetType"; 372 parameter["Required"] = true; 373 parameter["DataType"] = action_info::ParameterTypes::String; 374 375 nlohmann::json::array_t allowableValues; 376 allowableValues.emplace_back("GracefulRestart"); 377 allowableValues.emplace_back("ForceRestart"); 378 parameter["AllowableValues"] = std::move(allowableValues); 379 380 nlohmann::json::array_t parameters; 381 parameters.emplace_back(std::move(parameter)); 382 383 asyncResp->res.jsonValue["Parameters"] = std::move(parameters); 384 }); 385 } 386 387 /** 388 * @brief Retrieves BMC manager location data over DBus 389 * 390 * @param[in] asyncResp Shared pointer for completing asynchronous calls 391 * @param[in] connectionName - service name 392 * @param[in] path - object path 393 * @return none 394 */ 395 inline void getLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 396 const std::string& connectionName, 397 const std::string& path) 398 { 399 BMCWEB_LOG_DEBUG("Get BMC manager Location data."); 400 401 dbus::utility::getProperty<std::string>( 402 connectionName, path, 403 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 404 [asyncResp](const boost::system::error_code& ec, 405 const std::string& property) { 406 if (ec) 407 { 408 BMCWEB_LOG_DEBUG("DBUS response error for " 409 "Location"); 410 messages::internalError(asyncResp->res); 411 return; 412 } 413 414 asyncResp->res 415 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 416 property; 417 }); 418 } 419 // avoid name collision systems.hpp 420 inline void managerGetLastResetTime( 421 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 422 { 423 BMCWEB_LOG_DEBUG("Getting Manager Last Reset Time"); 424 425 dbus::utility::getProperty<uint64_t>( 426 "xyz.openbmc_project.State.BMC", "/xyz/openbmc_project/state/bmc0", 427 "xyz.openbmc_project.State.BMC", "LastRebootTime", 428 [asyncResp](const boost::system::error_code& ec, 429 const uint64_t lastResetTime) { 430 if (ec) 431 { 432 BMCWEB_LOG_DEBUG("D-BUS response error {}", ec); 433 return; 434 } 435 436 // LastRebootTime is epoch time, in milliseconds 437 // https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19 438 uint64_t lastResetTimeStamp = lastResetTime / 1000; 439 440 // Convert to ISO 8601 standard 441 asyncResp->res.jsonValue["LastResetTime"] = 442 redfish::time_utils::getDateTimeUint(lastResetTimeStamp); 443 }); 444 } 445 446 /** 447 * @brief Set the running firmware image 448 * 449 * @param[i,o] asyncResp - Async response object 450 * @param[i] runningFirmwareTarget - Image to make the running image 451 * 452 * @return void 453 */ 454 inline void setActiveFirmwareImage( 455 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 456 const std::string& runningFirmwareTarget) 457 { 458 // Get the Id from /redfish/v1/UpdateService/FirmwareInventory/<Id> 459 std::string::size_type idPos = runningFirmwareTarget.rfind('/'); 460 if (idPos == std::string::npos) 461 { 462 messages::propertyValueNotInList(asyncResp->res, runningFirmwareTarget, 463 "@odata.id"); 464 BMCWEB_LOG_DEBUG("Can't parse firmware ID!"); 465 return; 466 } 467 idPos++; 468 if (idPos >= runningFirmwareTarget.size()) 469 { 470 messages::propertyValueNotInList(asyncResp->res, runningFirmwareTarget, 471 "@odata.id"); 472 BMCWEB_LOG_DEBUG("Invalid firmware ID."); 473 return; 474 } 475 std::string firmwareId = runningFirmwareTarget.substr(idPos); 476 477 // Make sure the image is valid before setting priority 478 sdbusplus::message::object_path objPath("/xyz/openbmc_project/software"); 479 dbus::utility::getManagedObjects( 480 getBMCUpdateServiceName(), objPath, 481 [asyncResp, firmwareId, runningFirmwareTarget]( 482 const boost::system::error_code& ec, 483 const dbus::utility::ManagedObjectType& subtree) { 484 if (ec) 485 { 486 BMCWEB_LOG_DEBUG("D-Bus response error getting objects."); 487 messages::internalError(asyncResp->res); 488 return; 489 } 490 491 if (subtree.empty()) 492 { 493 BMCWEB_LOG_DEBUG("Can't find image!"); 494 messages::internalError(asyncResp->res); 495 return; 496 } 497 498 bool foundImage = false; 499 for (const auto& object : subtree) 500 { 501 const std::string& path = 502 static_cast<const std::string&>(object.first); 503 std::size_t idPos2 = path.rfind('/'); 504 505 if (idPos2 == std::string::npos) 506 { 507 continue; 508 } 509 510 idPos2++; 511 if (idPos2 >= path.size()) 512 { 513 continue; 514 } 515 516 if (path.substr(idPos2) == firmwareId) 517 { 518 foundImage = true; 519 break; 520 } 521 } 522 523 if (!foundImage) 524 { 525 messages::propertyValueNotInList( 526 asyncResp->res, runningFirmwareTarget, "@odata.id"); 527 BMCWEB_LOG_DEBUG("Invalid firmware ID."); 528 return; 529 } 530 531 BMCWEB_LOG_DEBUG("Setting firmware version {} to priority 0.", 532 firmwareId); 533 534 // Only support Immediate 535 // An addition could be a Redfish Setting like 536 // ActiveSoftwareImageApplyTime and support OnReset 537 sdbusplus::asio::setProperty( 538 *crow::connections::systemBus, getBMCUpdateServiceName(), 539 "/xyz/openbmc_project/software/" + firmwareId, 540 "xyz.openbmc_project.Software.RedundancyPriority", "Priority", 541 static_cast<uint8_t>(0), 542 [asyncResp](const boost::system::error_code& ec2) { 543 if (ec2) 544 { 545 BMCWEB_LOG_DEBUG("D-Bus response error setting."); 546 messages::internalError(asyncResp->res); 547 return; 548 } 549 doBMCGracefulRestart(asyncResp); 550 }); 551 }); 552 } 553 554 inline void afterSetDateTime( 555 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 556 const boost::system::error_code& ec, const sdbusplus::message_t& msg) 557 { 558 if (ec) 559 { 560 BMCWEB_LOG_DEBUG("Failed to set elapsed time. DBUS response error {}", 561 ec); 562 const sd_bus_error* dbusError = msg.get_error(); 563 if (dbusError != nullptr) 564 { 565 std::string_view errorName(dbusError->name); 566 if (errorName == 567 "org.freedesktop.timedate1.AutomaticTimeSyncEnabled") 568 { 569 BMCWEB_LOG_DEBUG("Setting conflict"); 570 messages::propertyValueConflict( 571 asyncResp->res, "DateTime", 572 "Managers/NetworkProtocol/NTPProcotolEnabled"); 573 return; 574 } 575 } 576 messages::internalError(asyncResp->res); 577 return; 578 } 579 asyncResp->res.result(boost::beast::http::status::no_content); 580 } 581 582 inline void setDateTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 583 const std::string& datetime) 584 { 585 BMCWEB_LOG_DEBUG("Set date time: {}", datetime); 586 587 std::optional<redfish::time_utils::usSinceEpoch> us = 588 redfish::time_utils::dateStringToEpoch(datetime); 589 if (!us) 590 { 591 messages::propertyValueFormatError(asyncResp->res, datetime, 592 "DateTime"); 593 return; 594 } 595 // Set the absolute datetime 596 bool relative = false; 597 bool interactive = false; 598 crow::connections::systemBus->async_method_call( 599 [asyncResp](const boost::system::error_code& ec, 600 const sdbusplus::message_t& msg) { 601 afterSetDateTime(asyncResp, ec, msg); 602 }, 603 "org.freedesktop.timedate1", "/org/freedesktop/timedate1", 604 "org.freedesktop.timedate1", "SetTime", us->count(), relative, 605 interactive); 606 } 607 608 inline void checkForQuiesced( 609 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 610 { 611 dbus::utility::getProperty<std::string>( 612 "org.freedesktop.systemd1", 613 "/org/freedesktop/systemd1/unit/obmc-bmc-service-quiesce@0.target", 614 "org.freedesktop.systemd1.Unit", "ActiveState", 615 [asyncResp](const boost::system::error_code& ec, 616 const std::string& val) { 617 if (!ec) 618 { 619 if (val == "active") 620 { 621 asyncResp->res.jsonValue["Status"]["Health"] = 622 resource::Health::Critical; 623 asyncResp->res.jsonValue["Status"]["State"] = 624 resource::State::Quiesced; 625 return; 626 } 627 } 628 asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; 629 asyncResp->res.jsonValue["Status"]["State"] = 630 resource::State::Enabled; 631 }); 632 } 633 634 inline void getPhysicalAssets( 635 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 636 const boost::system::error_code& ec, 637 const dbus::utility::DBusPropertiesMap& propertiesList) 638 { 639 if (ec) 640 { 641 BMCWEB_LOG_DEBUG("Can't get bmc asset!"); 642 return; 643 } 644 645 const std::string* partNumber = nullptr; 646 const std::string* serialNumber = nullptr; 647 const std::string* manufacturer = nullptr; 648 const std::string* model = nullptr; 649 const std::string* sparePartNumber = nullptr; 650 651 const bool success = sdbusplus::unpackPropertiesNoThrow( 652 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber", 653 partNumber, "SerialNumber", serialNumber, "Manufacturer", manufacturer, 654 "Model", model, "SparePartNumber", sparePartNumber); 655 656 if (!success) 657 { 658 messages::internalError(asyncResp->res); 659 return; 660 } 661 662 if (partNumber != nullptr) 663 { 664 asyncResp->res.jsonValue["PartNumber"] = *partNumber; 665 } 666 667 if (serialNumber != nullptr) 668 { 669 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; 670 } 671 672 if (manufacturer != nullptr) 673 { 674 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; 675 } 676 677 if (model != nullptr) 678 { 679 asyncResp->res.jsonValue["Model"] = *model; 680 } 681 682 if (sparePartNumber != nullptr) 683 { 684 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; 685 } 686 } 687 688 inline void getManagerData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 689 const std::string& managerPath, 690 const dbus::utility::MapperServiceMap& serviceMap) 691 { 692 if (managerPath.empty() || serviceMap.size() != 1) 693 { 694 BMCWEB_LOG_DEBUG("Error getting bmc D-Bus object!"); 695 messages::internalError(asyncResp->res); 696 return; 697 } 698 699 for (const auto& [connectionName, interfaces] : serviceMap) 700 { 701 for (const auto& interfaceName : interfaces) 702 { 703 if (interfaceName == 704 "xyz.openbmc_project.Inventory.Decorator.Asset") 705 { 706 dbus::utility::getAllProperties( 707 *crow::connections::systemBus, connectionName, managerPath, 708 "xyz.openbmc_project.Inventory.Decorator.Asset", 709 std::bind_front(getPhysicalAssets, asyncResp)); 710 } 711 else if (interfaceName == 712 "xyz.openbmc_project.Inventory.Decorator.LocationCode") 713 { 714 getLocation(asyncResp, connectionName, managerPath); 715 } 716 else if (interfaceName == 717 "xyz.openbmc_project.Association.Definitions") 718 { 719 getLocationIndicatorActive(asyncResp, managerPath); 720 } 721 } 722 } 723 } 724 725 inline void afterGetManagerObject( 726 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 727 const boost::system::error_code& ec, 728 const dbus::utility::MapperGetSubTreeResponse& subtree, 729 const std::function< 730 void(const std::string& managerPath, 731 const dbus::utility::MapperServiceMap& serviceMap)>& callback) 732 { 733 if (ec) 734 { 735 BMCWEB_LOG_DEBUG("D-Bus response error on GetSubTree {}", ec); 736 return; 737 } 738 if (subtree.empty()) 739 { 740 BMCWEB_LOG_DEBUG("Can't find bmc D-Bus object!"); 741 return; 742 } 743 // Assume only 1 bmc D-Bus object 744 // Throw an error if there is more than 1 745 if (subtree.size() > 1) 746 { 747 BMCWEB_LOG_ERROR("Found more than 1 bmc D-Bus object!"); 748 messages::internalError(asyncResp->res); 749 return; 750 } 751 752 callback(subtree[0].first, subtree[0].second); 753 } 754 755 inline void getManagerObject( 756 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 757 const std::string& /* managerId */, 758 std::function<void(const std::string& managerPath, 759 const dbus::utility::MapperServiceMap& serviceMap)>&& 760 callback) 761 { 762 constexpr std::array<std::string_view, 1> interfaces = { 763 "xyz.openbmc_project.Inventory.Item.Bmc"}; 764 dbus::utility::getSubTree( 765 "/xyz/openbmc_project/inventory", 0, interfaces, 766 [asyncResp, callback{std::move(callback)}]( 767 const boost::system::error_code& ec, 768 const dbus::utility::MapperGetSubTreeResponse& subtree) { 769 afterGetManagerObject(asyncResp, ec, subtree, callback); 770 }); 771 } 772 773 inline void handleManagerGet( 774 App& app, const crow::Request& req, 775 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 776 const std::string& managerId) 777 { 778 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 779 { 780 return; 781 } 782 783 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 784 { 785 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 786 return; 787 } 788 789 std::string uuid = persistent_data::getConfig().systemUuid; 790 791 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 792 "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME); 793 asyncResp->res.jsonValue["@odata.type"] = "#Manager.v1_15_0.Manager"; 794 asyncResp->res.jsonValue["Id"] = BMCWEB_REDFISH_MANAGER_URI_NAME; 795 asyncResp->res.jsonValue["Name"] = "OpenBmc Manager"; 796 asyncResp->res.jsonValue["Description"] = "Baseboard Management Controller"; 797 asyncResp->res.jsonValue["PowerState"] = resource::PowerState::On; 798 799 asyncResp->res.jsonValue["ManagerType"] = manager::ManagerType::BMC; 800 asyncResp->res.jsonValue["UUID"] = systemd_utils::getUuid(); 801 asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid; 802 asyncResp->res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model 803 804 asyncResp->res.jsonValue["LogServices"]["@odata.id"] = boost::urls::format( 805 "/redfish/v1/Managers/{}/LogServices", BMCWEB_REDFISH_MANAGER_URI_NAME); 806 asyncResp->res.jsonValue["NetworkProtocol"]["@odata.id"] = 807 boost::urls::format("/redfish/v1/Managers/{}/NetworkProtocol", 808 BMCWEB_REDFISH_MANAGER_URI_NAME); 809 asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = 810 boost::urls::format("/redfish/v1/Managers/{}/EthernetInterfaces", 811 BMCWEB_REDFISH_MANAGER_URI_NAME); 812 813 manager_utils::getServiceIdentification(asyncResp, false); 814 815 if constexpr (BMCWEB_VM_NBDPROXY) 816 { 817 asyncResp->res.jsonValue["VirtualMedia"]["@odata.id"] = 818 boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia", 819 BMCWEB_REDFISH_MANAGER_URI_NAME); 820 } 821 822 // Manager.Reset (an action) can be many values, OpenBMC only 823 // supports BMC reboot. 824 nlohmann::json& managerReset = 825 asyncResp->res.jsonValue["Actions"]["#Manager.Reset"]; 826 managerReset["target"] = 827 boost::urls::format("/redfish/v1/Managers/{}/Actions/Manager.Reset", 828 BMCWEB_REDFISH_MANAGER_URI_NAME); 829 managerReset["@Redfish.ActionInfo"] = 830 boost::urls::format("/redfish/v1/Managers/{}/ResetActionInfo", 831 BMCWEB_REDFISH_MANAGER_URI_NAME); 832 833 // ResetToDefaults (Factory Reset) has values like 834 // PreserveNetworkAndUsers and PreserveNetwork that aren't supported 835 // on OpenBMC 836 nlohmann::json& resetToDefaults = 837 asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"]; 838 resetToDefaults["target"] = boost::urls::format( 839 "/redfish/v1/Managers/{}/Actions/Manager.ResetToDefaults", 840 BMCWEB_REDFISH_MANAGER_URI_NAME); 841 resetToDefaults["ResetType@Redfish.AllowableValues"] = 842 nlohmann::json::array_t({"ResetAll"}); 843 844 std::pair<std::string, std::string> redfishDateTimeOffset = 845 redfish::time_utils::getDateTimeOffsetNow(); 846 847 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 848 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 849 redfishDateTimeOffset.second; 850 851 if constexpr (BMCWEB_KVM) 852 { 853 // Fill in GraphicalConsole info 854 asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true; 855 asyncResp->res.jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 856 4; 857 asyncResp->res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = 858 nlohmann::json::array_t({"KVMIP"}); 859 } 860 if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 861 { 862 asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1; 863 864 nlohmann::json::array_t managerForServers; 865 nlohmann::json::object_t manager; 866 manager["@odata.id"] = std::format("/redfish/v1/Systems/{}", 867 BMCWEB_REDFISH_SYSTEM_URI_NAME); 868 managerForServers.emplace_back(std::move(manager)); 869 870 asyncResp->res.jsonValue["Links"]["ManagerForServers"] = 871 std::move(managerForServers); 872 } 873 874 sw_util::populateSoftwareInformation(asyncResp, sw_util::bmcPurpose, 875 "FirmwareVersion", true); 876 877 managerGetLastResetTime(asyncResp); 878 879 // ManagerDiagnosticData is added for all BMCs. 880 nlohmann::json& managerDiagnosticData = 881 asyncResp->res.jsonValue["ManagerDiagnosticData"]; 882 managerDiagnosticData["@odata.id"] = 883 boost::urls::format("/redfish/v1/Managers/{}/ManagerDiagnosticData", 884 BMCWEB_REDFISH_MANAGER_URI_NAME); 885 886 getMainChassisId( 887 asyncResp, [](const std::string& chassisId, 888 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { 889 aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1; 890 nlohmann::json::array_t managerForChassis; 891 nlohmann::json::object_t managerObj; 892 boost::urls::url chassiUrl = 893 boost::urls::format("/redfish/v1/Chassis/{}", chassisId); 894 managerObj["@odata.id"] = chassiUrl; 895 managerForChassis.emplace_back(std::move(managerObj)); 896 aRsp->res.jsonValue["Links"]["ManagerForChassis"] = 897 std::move(managerForChassis); 898 aRsp->res.jsonValue["Links"]["ManagerInChassis"]["@odata.id"] = 899 chassiUrl; 900 }); 901 902 dbus::utility::getProperty<double>( 903 "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 904 "org.freedesktop.systemd1.Manager", "Progress", 905 [asyncResp](const boost::system::error_code& ec, double val) { 906 if (ec) 907 { 908 BMCWEB_LOG_ERROR("Error while getting progress"); 909 messages::internalError(asyncResp->res); 910 return; 911 } 912 if (val < 1.0) 913 { 914 asyncResp->res.jsonValue["Status"]["Health"] = 915 resource::Health::OK; 916 asyncResp->res.jsonValue["Status"]["State"] = 917 resource::State::Starting; 918 return; 919 } 920 checkForQuiesced(asyncResp); 921 }); 922 923 getManagerObject(asyncResp, managerId, 924 std::bind_front(getManagerData, asyncResp)); 925 926 RedfishService::getInstance(app).handleSubRoute(req, asyncResp); 927 } 928 929 inline void handleManagerPatch( 930 App& app, const crow::Request& req, 931 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 932 const std::string& managerId) 933 { 934 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 935 { 936 return; 937 } 938 939 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 940 { 941 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 942 return; 943 } 944 945 std::optional<std::string> activeSoftwareImageOdataId; 946 std::optional<std::string> datetime; 947 std::optional<bool> locationIndicatorActive; 948 std::optional<nlohmann::json::object_t> pidControllers; 949 std::optional<nlohmann::json::object_t> fanControllers; 950 std::optional<nlohmann::json::object_t> fanZones; 951 std::optional<nlohmann::json::object_t> stepwiseControllers; 952 std::optional<std::string> profile; 953 std::optional<std::string> serviceIdentification; 954 955 if (!json_util::readJsonPatch( // 956 req, asyncResp->res, // 957 "DateTime", datetime, // 958 "Links/ActiveSoftwareImage/@odata.id", 959 activeSoftwareImageOdataId, // 960 "LocationIndicatorActive", 961 locationIndicatorActive, // 962 "Oem/OpenBmc/Fan/FanControllers", fanControllers, // 963 "Oem/OpenBmc/Fan/FanZones", fanZones, // 964 "Oem/OpenBmc/Fan/PidControllers", pidControllers, // 965 "Oem/OpenBmc/Fan/Profile", profile, // 966 "Oem/OpenBmc/Fan/StepwiseControllers", 967 stepwiseControllers, // 968 "ServiceIdentification", serviceIdentification // 969 )) 970 { 971 return; 972 } 973 974 if (activeSoftwareImageOdataId) 975 { 976 setActiveFirmwareImage(asyncResp, *activeSoftwareImageOdataId); 977 } 978 979 if (datetime) 980 { 981 setDateTime(asyncResp, *datetime); 982 } 983 984 if (locationIndicatorActive) 985 { 986 setLocationIndicatorActiveState(asyncResp, *locationIndicatorActive, 987 managerId); 988 } 989 990 if (serviceIdentification) 991 { 992 manager_utils::setServiceIdentification(asyncResp, 993 serviceIdentification.value()); 994 } 995 996 RedfishService::getInstance(app).handleSubRoute(req, asyncResp); 997 } 998 999 inline void handleManagerCollectionGet( 1000 crow::App& app, const crow::Request& req, 1001 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1002 { 1003 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1004 { 1005 return; 1006 } 1007 // Collections don't include the static data added by SubRoute 1008 // because it has a duplicate entry for members 1009 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers"; 1010 asyncResp->res.jsonValue["@odata.type"] = 1011 "#ManagerCollection.ManagerCollection"; 1012 asyncResp->res.jsonValue["Name"] = "Manager Collection"; 1013 asyncResp->res.jsonValue["Members@odata.count"] = 1; 1014 nlohmann::json::array_t members; 1015 nlohmann::json& bmc = members.emplace_back(); 1016 bmc["@odata.id"] = boost::urls::format("/redfish/v1/Managers/{}", 1017 BMCWEB_REDFISH_MANAGER_URI_NAME); 1018 asyncResp->res.jsonValue["Members"] = std::move(members); 1019 } 1020 1021 inline void requestRoutesManager(App& app) 1022 { 1023 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/") 1024 .privileges(redfish::privileges::getManager) 1025 .methods(boost::beast::http::verb::get)( 1026 std::bind_front(handleManagerGet, std::ref(app))); 1027 1028 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/") 1029 .privileges(redfish::privileges::patchManager) 1030 .methods(boost::beast::http::verb::patch)( 1031 std::bind_front(handleManagerPatch, std::ref(app))); 1032 } 1033 1034 inline void requestRoutesManagerCollection(App& app) 1035 { 1036 BMCWEB_ROUTE(app, "/redfish/v1/Managers/") 1037 .privileges(redfish::privileges::getManagerCollection) 1038 .methods(boost::beast::http::verb::get)( 1039 std::bind_front(handleManagerCollectionGet, std::ref(app))); 1040 } 1041 } // namespace redfish 1042