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