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