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