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