11bb1a50fSOliver Brewka // SPDX-License-Identifier: Apache-2.0 21bb1a50fSOliver Brewka // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3fc5ae94dSOliver Brewka #pragma once 4fc5ae94dSOliver Brewka 5fc5ae94dSOliver Brewka #include "bmcweb_config.h" 6fc5ae94dSOliver Brewka 7fc5ae94dSOliver Brewka #include "async_resp.hpp" 82fdbc1f3SOliver Brewka #include "dbus_singleton.hpp" 9fc5ae94dSOliver Brewka #include "dbus_utility.hpp" 10fc5ae94dSOliver Brewka #include "error_messages.hpp" 11fc5ae94dSOliver Brewka #include "human_sort.hpp" 12fc5ae94dSOliver Brewka #include "logging.hpp" 13fc5ae94dSOliver Brewka #include "utility.hpp" 14fc5ae94dSOliver Brewka 15fc5ae94dSOliver Brewka #include <boost/url/format.hpp> 16fc5ae94dSOliver Brewka #include <boost/url/url.hpp> 17fc5ae94dSOliver Brewka #include <sdbusplus/message/native_types.hpp> 18fc5ae94dSOliver Brewka 19fc5ae94dSOliver Brewka #include <algorithm> 20fc5ae94dSOliver Brewka #include <array> 212fdbc1f3SOliver Brewka #include <cstdint> 22fc5ae94dSOliver Brewka #include <functional> 23fc5ae94dSOliver Brewka #include <memory> 24fc5ae94dSOliver Brewka #include <string> 25fc5ae94dSOliver Brewka #include <string_view> 26fc5ae94dSOliver Brewka #include <utility> 27fc5ae94dSOliver Brewka #include <vector> 28fc5ae94dSOliver Brewka 29fc5ae94dSOliver Brewka namespace redfish 30fc5ae94dSOliver Brewka { 31fc5ae94dSOliver Brewka 32fc5ae94dSOliver Brewka inline void handleSystemCollectionMembers( 33fc5ae94dSOliver Brewka const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 34fc5ae94dSOliver Brewka const boost::system::error_code& ec, 35fc5ae94dSOliver Brewka const dbus::utility::MapperGetSubTreePathsResponse& objects) 36fc5ae94dSOliver Brewka { 37fc5ae94dSOliver Brewka if (ec == boost::system::errc::io_error) 38fc5ae94dSOliver Brewka { 39fc5ae94dSOliver Brewka asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 40fc5ae94dSOliver Brewka asyncResp->res.jsonValue["Members@odata.count"] = 0; 41fc5ae94dSOliver Brewka return; 42fc5ae94dSOliver Brewka } 43fc5ae94dSOliver Brewka 44fc5ae94dSOliver Brewka if (ec) 45fc5ae94dSOliver Brewka { 46fc5ae94dSOliver Brewka BMCWEB_LOG_ERROR("DBUS response error {}", ec.value()); 47fc5ae94dSOliver Brewka messages::internalError(asyncResp->res); 48fc5ae94dSOliver Brewka return; 49fc5ae94dSOliver Brewka } 50fc5ae94dSOliver Brewka 51fc5ae94dSOliver Brewka nlohmann::json& membersArray = asyncResp->res.jsonValue["Members"]; 52fc5ae94dSOliver Brewka membersArray = nlohmann::json::array(); 53fc5ae94dSOliver Brewka 54fc5ae94dSOliver Brewka // consider an empty result as single-host, since single-host systems 55fc5ae94dSOliver Brewka // do not populate the ManagedHost dbus interface 56fc5ae94dSOliver Brewka if (objects.empty()) 57fc5ae94dSOliver Brewka { 58fc5ae94dSOliver Brewka asyncResp->res.jsonValue["Members@odata.count"] = 1; 59fc5ae94dSOliver Brewka nlohmann::json::object_t system; 60fc5ae94dSOliver Brewka system["@odata.id"] = boost::urls::format( 61fc5ae94dSOliver Brewka "/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME); 62fc5ae94dSOliver Brewka membersArray.emplace_back(std::move(system)); 63fc5ae94dSOliver Brewka 64fc5ae94dSOliver Brewka if constexpr (BMCWEB_HYPERVISOR_COMPUTER_SYSTEM) 65fc5ae94dSOliver Brewka { 66fc5ae94dSOliver Brewka BMCWEB_LOG_DEBUG("Hypervisor is available"); 67fc5ae94dSOliver Brewka asyncResp->res.jsonValue["Members@odata.count"] = 2; 68fc5ae94dSOliver Brewka 69fc5ae94dSOliver Brewka nlohmann::json::object_t hypervisor; 70fc5ae94dSOliver Brewka hypervisor["@odata.id"] = "/redfish/v1/Systems/hypervisor"; 71fc5ae94dSOliver Brewka membersArray.emplace_back(std::move(hypervisor)); 72fc5ae94dSOliver Brewka } 73fc5ae94dSOliver Brewka 74fc5ae94dSOliver Brewka return; 75fc5ae94dSOliver Brewka } 76fc5ae94dSOliver Brewka 77fc5ae94dSOliver Brewka std::vector<std::string> pathNames; 78fc5ae94dSOliver Brewka for (const auto& object : objects) 79fc5ae94dSOliver Brewka { 80fc5ae94dSOliver Brewka sdbusplus::message::object_path path(object); 81fc5ae94dSOliver Brewka std::string leaf = path.filename(); 82fc5ae94dSOliver Brewka if (leaf.empty()) 83fc5ae94dSOliver Brewka { 84fc5ae94dSOliver Brewka continue; 85fc5ae94dSOliver Brewka } 86fc5ae94dSOliver Brewka pathNames.emplace_back(leaf); 87fc5ae94dSOliver Brewka } 88fc5ae94dSOliver Brewka std::ranges::sort(pathNames, AlphanumLess<std::string>()); 89fc5ae94dSOliver Brewka 90fc5ae94dSOliver Brewka for (const std::string& systemName : pathNames) 91fc5ae94dSOliver Brewka { 92fc5ae94dSOliver Brewka nlohmann::json::object_t member; 936b90272cSOliver Brewka member["@odata.id"] = 946b90272cSOliver Brewka boost::urls::format("/redfish/v1/Systems/{}", systemName); 95fc5ae94dSOliver Brewka membersArray.emplace_back(std::move(member)); 96fc5ae94dSOliver Brewka } 97fc5ae94dSOliver Brewka asyncResp->res.jsonValue["Members@odata.count"] = membersArray.size(); 98fc5ae94dSOliver Brewka } 99fc5ae94dSOliver Brewka 100fc5ae94dSOliver Brewka /** 101fc5ae94dSOliver Brewka * @brief Populate the system collection members from a GetSubTreePaths search 102fc5ae94dSOliver Brewka * of the inventory based of the ManagedHost dbus interface 103fc5ae94dSOliver Brewka * 104fc5ae94dSOliver Brewka * @param[i] asyncResp Async response object 105fc5ae94dSOliver Brewka * 106fc5ae94dSOliver Brewka * @return None 107fc5ae94dSOliver Brewka */ 108fc5ae94dSOliver Brewka inline void getSystemCollectionMembers( 109fc5ae94dSOliver Brewka const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 110fc5ae94dSOliver Brewka { 111fc5ae94dSOliver Brewka constexpr std::array<std::string_view, 1> interfaces{ 112fc5ae94dSOliver Brewka "xyz.openbmc_project.Inventory.Decorator.ManagedHost", 113fc5ae94dSOliver Brewka }; 114fc5ae94dSOliver Brewka 115fc5ae94dSOliver Brewka BMCWEB_LOG_DEBUG("Get system collection members for /redfish/v1/Systems"); 116fc5ae94dSOliver Brewka 117fc5ae94dSOliver Brewka dbus::utility::getSubTreePaths( 118fc5ae94dSOliver Brewka "/xyz/openbmc_project/inventory", 0, interfaces, 119fc5ae94dSOliver Brewka std::bind_front(handleSystemCollectionMembers, asyncResp)); 120fc5ae94dSOliver Brewka } 1212fdbc1f3SOliver Brewka 1222fdbc1f3SOliver Brewka inline void getManagedHostProperty( 1232fdbc1f3SOliver Brewka const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1242fdbc1f3SOliver Brewka const std::string& systemName, const std::string& systemPath, 1252fdbc1f3SOliver Brewka std::function<void(const uint64_t computerSystemIndex)> callback) 1262fdbc1f3SOliver Brewka { 1272fdbc1f3SOliver Brewka dbus::utility::getProperty<uint64_t>( 1282fdbc1f3SOliver Brewka "xyz.openbmc_project.EntityManager", systemPath, 1292fdbc1f3SOliver Brewka "xyz.openbmc_project.Inventory.Decorator.ManagedHost", "HostIndex", 1302fdbc1f3SOliver Brewka [asyncResp, systemName, systemPath, callback = std::move(callback)]( 1312fdbc1f3SOliver Brewka const boost::system::error_code& ec, const uint64_t hostIndex) { 1322fdbc1f3SOliver Brewka if (ec) 1332fdbc1f3SOliver Brewka { 1342fdbc1f3SOliver Brewka BMCWEB_LOG_WARNING("DBUS response error {}", ec); 1352fdbc1f3SOliver Brewka messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1362fdbc1f3SOliver Brewka systemName); 1372fdbc1f3SOliver Brewka return; 1382fdbc1f3SOliver Brewka } 1392fdbc1f3SOliver Brewka BMCWEB_LOG_DEBUG("Got index {} for path {}", hostIndex, systemPath); 1402fdbc1f3SOliver Brewka callback(hostIndex); 1412fdbc1f3SOliver Brewka }); 1422fdbc1f3SOliver Brewka } 1432fdbc1f3SOliver Brewka 1442fdbc1f3SOliver Brewka inline void afterGetComputerSystemSubTreePaths( 1452fdbc1f3SOliver Brewka const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1462fdbc1f3SOliver Brewka const std::string& systemName, 1472fdbc1f3SOliver Brewka std::function<void(const uint64_t computerSystemIndex)>& callback, 1482fdbc1f3SOliver Brewka const boost::system::error_code& ec, 1492fdbc1f3SOliver Brewka const dbus::utility::MapperGetSubTreePathsResponse& objects) 1502fdbc1f3SOliver Brewka { 1512fdbc1f3SOliver Brewka sdbusplus::message::object_path systemPath; 1522fdbc1f3SOliver Brewka if (ec) 1532fdbc1f3SOliver Brewka { 1542fdbc1f3SOliver Brewka if (ec.value() == boost::system::errc::io_error) 1552fdbc1f3SOliver Brewka { 1562fdbc1f3SOliver Brewka BMCWEB_LOG_WARNING("EIO - System not found"); 1572fdbc1f3SOliver Brewka messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1582fdbc1f3SOliver Brewka systemName); 1592fdbc1f3SOliver Brewka return; 1602fdbc1f3SOliver Brewka } 1612fdbc1f3SOliver Brewka 1622fdbc1f3SOliver Brewka BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value()); 1632fdbc1f3SOliver Brewka messages::internalError(asyncResp->res); 1642fdbc1f3SOliver Brewka return; 1652fdbc1f3SOliver Brewka } 1662fdbc1f3SOliver Brewka 1672fdbc1f3SOliver Brewka const auto& found = std::ranges::find_if( 1682fdbc1f3SOliver Brewka objects, [systemName](const sdbusplus::message::object_path& path) { 1692fdbc1f3SOliver Brewka return path.filename() == systemName; 1702fdbc1f3SOliver Brewka }); 1712fdbc1f3SOliver Brewka 1722fdbc1f3SOliver Brewka if (found == objects.end()) 1732fdbc1f3SOliver Brewka { 1742fdbc1f3SOliver Brewka BMCWEB_LOG_WARNING("Failed to match systemName: {}", systemName); 1752fdbc1f3SOliver Brewka messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1762fdbc1f3SOliver Brewka systemName); 1772fdbc1f3SOliver Brewka return; 1782fdbc1f3SOliver Brewka } 1792fdbc1f3SOliver Brewka 1802fdbc1f3SOliver Brewka systemPath = *found; 1812fdbc1f3SOliver Brewka 1822fdbc1f3SOliver Brewka getManagedHostProperty(asyncResp, systemName, systemPath, 1832fdbc1f3SOliver Brewka std::move(callback)); 1842fdbc1f3SOliver Brewka } 1852fdbc1f3SOliver Brewka 1862fdbc1f3SOliver Brewka /** 1872fdbc1f3SOliver Brewka * @brief Retrieve the index associated with the requested system based on the 1882fdbc1f3SOliver Brewka * ManagedHost interface 1892fdbc1f3SOliver Brewka * 1902fdbc1f3SOliver Brewka * @param[i] asyncResp Async response object 1912fdbc1f3SOliver Brewka * @param[i] systemName The requested system 1922fdbc1f3SOliver Brewka * @param[i] callback Callback to call once the index has been found 1932fdbc1f3SOliver Brewka * 1942fdbc1f3SOliver Brewka * @return None 1952fdbc1f3SOliver Brewka */ 1962fdbc1f3SOliver Brewka inline void getComputerSystemIndex( 1972fdbc1f3SOliver Brewka const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1982fdbc1f3SOliver Brewka const std::string& systemName, 1992fdbc1f3SOliver Brewka std::function<void(const uint64_t computerSystemIndex)>&& callback) 2002fdbc1f3SOliver Brewka { 2012fdbc1f3SOliver Brewka if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2022fdbc1f3SOliver Brewka { 2032fdbc1f3SOliver Brewka constexpr std::array<std::string_view, 1> interfaces{ 2042fdbc1f3SOliver Brewka "xyz.openbmc_project.Inventory.Decorator.ManagedHost"}; 2052fdbc1f3SOliver Brewka dbus::utility::getSubTreePaths( 2062fdbc1f3SOliver Brewka "/xyz/openbmc_project/inventory", 0, interfaces, 2072fdbc1f3SOliver Brewka std::bind_front(afterGetComputerSystemSubTreePaths, asyncResp, 2082fdbc1f3SOliver Brewka systemName, std::move(callback))); 2092fdbc1f3SOliver Brewka } 2102fdbc1f3SOliver Brewka else 2112fdbc1f3SOliver Brewka { 2122fdbc1f3SOliver Brewka // on single-host, fallback to index 0 2132fdbc1f3SOliver Brewka BMCWEB_LOG_DEBUG( 2142fdbc1f3SOliver Brewka "Single-host detected, fallback to computerSystemIndex 0"); 2152fdbc1f3SOliver Brewka callback(0); 2162fdbc1f3SOliver Brewka } 2172fdbc1f3SOliver Brewka } 2182fdbc1f3SOliver Brewka 2192fdbc1f3SOliver Brewka inline sdbusplus::message::object_path getHostStateObjectPath( 2202fdbc1f3SOliver Brewka const uint64_t computerSystemIndex) 2212fdbc1f3SOliver Brewka { 2222fdbc1f3SOliver Brewka const sdbusplus::message::object_path hostStatePath( 2232fdbc1f3SOliver Brewka "/xyz/openbmc_project/state/host" + 2242fdbc1f3SOliver Brewka std::to_string(computerSystemIndex)); 2252fdbc1f3SOliver Brewka 2262fdbc1f3SOliver Brewka return hostStatePath; 2272fdbc1f3SOliver Brewka } 2282fdbc1f3SOliver Brewka 2292fdbc1f3SOliver Brewka inline std::string getHostStateServiceName(const uint64_t computerSystemIndex) 2302fdbc1f3SOliver Brewka { 2312fdbc1f3SOliver Brewka std::string hostStateService = "xyz.openbmc_project.State.Host"; 2322fdbc1f3SOliver Brewka if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2332fdbc1f3SOliver Brewka { 2342fdbc1f3SOliver Brewka hostStateService += std::to_string(computerSystemIndex); 2352fdbc1f3SOliver Brewka } 2362fdbc1f3SOliver Brewka 2372fdbc1f3SOliver Brewka return hostStateService; 2382fdbc1f3SOliver Brewka } 2392fdbc1f3SOliver Brewka 2402fdbc1f3SOliver Brewka inline sdbusplus::message::object_path getChassisStateObjectPath( 2412fdbc1f3SOliver Brewka const uint64_t computerSystemIndex) 2422fdbc1f3SOliver Brewka { 2432fdbc1f3SOliver Brewka const sdbusplus::message::object_path chassisStatePath( 2442fdbc1f3SOliver Brewka "/xyz/openbmc_project/state/chassis" + 2452fdbc1f3SOliver Brewka std::to_string(computerSystemIndex)); 2462fdbc1f3SOliver Brewka 2472fdbc1f3SOliver Brewka return chassisStatePath; 2482fdbc1f3SOliver Brewka } 2492fdbc1f3SOliver Brewka 2502fdbc1f3SOliver Brewka inline std::string getChassisStateServiceName( 2512fdbc1f3SOliver Brewka const uint64_t computerSystemIndex) 2522fdbc1f3SOliver Brewka { 2532fdbc1f3SOliver Brewka std::string chassisStateService = "xyz.openbmc_project.State.Chassis"; 2542fdbc1f3SOliver Brewka if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2552fdbc1f3SOliver Brewka { 2562fdbc1f3SOliver Brewka chassisStateService += std::to_string(computerSystemIndex); 2572fdbc1f3SOliver Brewka } 2582fdbc1f3SOliver Brewka 2592fdbc1f3SOliver Brewka return chassisStateService; 2602fdbc1f3SOliver Brewka } 261*2eaa9279SJanet Adkins 262*2eaa9279SJanet Adkins namespace systems_utils 263*2eaa9279SJanet Adkins { 264*2eaa9279SJanet Adkins 265*2eaa9279SJanet Adkins inline void afterGetValidSystemsPath( 266*2eaa9279SJanet Adkins const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 267*2eaa9279SJanet Adkins const std::string& systemId, 268*2eaa9279SJanet Adkins const std::function<void(const std::optional<std::string>&)>& callback, 269*2eaa9279SJanet Adkins const boost::system::error_code& ec, 270*2eaa9279SJanet Adkins const dbus::utility::MapperGetSubTreePathsResponse& systemsPaths) 271*2eaa9279SJanet Adkins { 272*2eaa9279SJanet Adkins if (ec) 273*2eaa9279SJanet Adkins { 274*2eaa9279SJanet Adkins if (ec == boost::system::errc::io_error) 275*2eaa9279SJanet Adkins { 276*2eaa9279SJanet Adkins BMCWEB_LOG_DEBUG("No systems found"); 277*2eaa9279SJanet Adkins callback(std::nullopt); 278*2eaa9279SJanet Adkins return; 279*2eaa9279SJanet Adkins } 280*2eaa9279SJanet Adkins BMCWEB_LOG_ERROR("DBUS response error: {}", ec.value()); 281*2eaa9279SJanet Adkins messages::internalError(asyncResp->res); 282*2eaa9279SJanet Adkins return; 283*2eaa9279SJanet Adkins } 284*2eaa9279SJanet Adkins 285*2eaa9279SJanet Adkins for (const std::string& system : systemsPaths) 286*2eaa9279SJanet Adkins { 287*2eaa9279SJanet Adkins sdbusplus::message::object_path path(system); 288*2eaa9279SJanet Adkins if (path.filename() == systemId) 289*2eaa9279SJanet Adkins { 290*2eaa9279SJanet Adkins callback(path); 291*2eaa9279SJanet Adkins return; 292*2eaa9279SJanet Adkins } 293*2eaa9279SJanet Adkins } 294*2eaa9279SJanet Adkins BMCWEB_LOG_DEBUG("No system named {} found", systemId); 295*2eaa9279SJanet Adkins callback(std::nullopt); 296*2eaa9279SJanet Adkins } 297*2eaa9279SJanet Adkins 298*2eaa9279SJanet Adkins inline void getValidSystemsPath( 299*2eaa9279SJanet Adkins const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 300*2eaa9279SJanet Adkins const std::string& systemId, 301*2eaa9279SJanet Adkins std::function<void(const std::optional<std::string>&)>&& callback) 302*2eaa9279SJanet Adkins { 303*2eaa9279SJanet Adkins BMCWEB_LOG_DEBUG("Get path for {}", systemId); 304*2eaa9279SJanet Adkins 305*2eaa9279SJanet Adkins constexpr std::array<std::string_view, 2> interfaces = { 306*2eaa9279SJanet Adkins "xyz.openbmc_project.Inventory.Decorator.ManagedHost", 307*2eaa9279SJanet Adkins "xyz.openbmc_project.Inventory.Item.System"}; 308*2eaa9279SJanet Adkins dbus::utility::getSubTreePaths( 309*2eaa9279SJanet Adkins "/xyz/openbmc_project/inventory", 0, interfaces, 310*2eaa9279SJanet Adkins [asyncResp, systemId, callback{std::move(callback)}]( 311*2eaa9279SJanet Adkins const boost::system::error_code& ec, 312*2eaa9279SJanet Adkins const dbus::utility::MapperGetSubTreePathsResponse& systemsPaths) { 313*2eaa9279SJanet Adkins afterGetValidSystemsPath(asyncResp, systemId, callback, ec, 314*2eaa9279SJanet Adkins systemsPaths); 315*2eaa9279SJanet Adkins }); 316*2eaa9279SJanet Adkins } 317*2eaa9279SJanet Adkins 318*2eaa9279SJanet Adkins } // namespace systems_utils 319fc5ae94dSOliver Brewka } // namespace redfish 320