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