xref: /openbmc/bmcweb/features/redfish/include/utils/systems_utils.hpp (revision 2eaa9279866425cbc463529d72d522f0f32caf1f)
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