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