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