xref: /openbmc/bmcweb/features/redfish/include/utils/systems_utils.hpp (revision 2fdbc1f3009eb7dbde40d703c978e1be7f50c574)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 #pragma once
4 
5 #include "bmcweb_config.h"
6 
7 #include "async_resp.hpp"
8 #include "dbus_singleton.hpp"
9 #include "dbus_utility.hpp"
10 #include "error_messages.hpp"
11 #include "human_sort.hpp"
12 #include "logging.hpp"
13 #include "utility.hpp"
14 
15 #include <boost/url/format.hpp>
16 #include <boost/url/url.hpp>
17 #include <sdbusplus/message/native_types.hpp>
18 
19 #include <algorithm>
20 #include <array>
21 #include <cstdint>
22 #include <functional>
23 #include <memory>
24 #include <string>
25 #include <string_view>
26 #include <utility>
27 #include <vector>
28 
29 namespace redfish
30 {
31 
32 inline void handleSystemCollectionMembers(
33     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
34     const boost::system::error_code& ec,
35     const dbus::utility::MapperGetSubTreePathsResponse& objects)
36 {
37     if (ec == boost::system::errc::io_error)
38     {
39         asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
40         asyncResp->res.jsonValue["Members@odata.count"] = 0;
41         return;
42     }
43 
44     if (ec)
45     {
46         BMCWEB_LOG_ERROR("DBUS response error {}", ec.value());
47         messages::internalError(asyncResp->res);
48         return;
49     }
50 
51     nlohmann::json& membersArray = asyncResp->res.jsonValue["Members"];
52     membersArray = nlohmann::json::array();
53 
54     // consider an empty result as single-host, since single-host systems
55     // do not populate the ManagedHost dbus interface
56     if (objects.empty())
57     {
58         asyncResp->res.jsonValue["Members@odata.count"] = 1;
59         nlohmann::json::object_t system;
60         system["@odata.id"] = boost::urls::format(
61             "/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME);
62         membersArray.emplace_back(std::move(system));
63 
64         if constexpr (BMCWEB_HYPERVISOR_COMPUTER_SYSTEM)
65         {
66             BMCWEB_LOG_DEBUG("Hypervisor is available");
67             asyncResp->res.jsonValue["Members@odata.count"] = 2;
68 
69             nlohmann::json::object_t hypervisor;
70             hypervisor["@odata.id"] = "/redfish/v1/Systems/hypervisor";
71             membersArray.emplace_back(std::move(hypervisor));
72         }
73 
74         return;
75     }
76 
77     std::vector<std::string> pathNames;
78     for (const auto& object : objects)
79     {
80         sdbusplus::message::object_path path(object);
81         std::string leaf = path.filename();
82         if (leaf.empty())
83         {
84             continue;
85         }
86         pathNames.emplace_back(leaf);
87     }
88     std::ranges::sort(pathNames, AlphanumLess<std::string>());
89 
90     for (const std::string& systemName : pathNames)
91     {
92         nlohmann::json::object_t member;
93         member["@odata.id"] =
94             boost::urls::format("/redfish/v1/Systems/{}", systemName);
95         membersArray.emplace_back(std::move(member));
96     }
97     asyncResp->res.jsonValue["Members@odata.count"] = membersArray.size();
98 }
99 
100 /**
101  * @brief Populate the system collection members from a GetSubTreePaths search
102  * of the inventory based of the ManagedHost dbus interface
103  *
104  * @param[i] asyncResp  Async response object
105  *
106  * @return None
107  */
108 inline void getSystemCollectionMembers(
109     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
110 {
111     constexpr std::array<std::string_view, 1> interfaces{
112         "xyz.openbmc_project.Inventory.Decorator.ManagedHost",
113     };
114 
115     BMCWEB_LOG_DEBUG("Get system collection members for /redfish/v1/Systems");
116 
117     dbus::utility::getSubTreePaths(
118         "/xyz/openbmc_project/inventory", 0, interfaces,
119         std::bind_front(handleSystemCollectionMembers, asyncResp));
120 }
121 
122 inline void getManagedHostProperty(
123     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
124     const std::string& systemName, const std::string& systemPath,
125     std::function<void(const uint64_t computerSystemIndex)> callback)
126 {
127     dbus::utility::getProperty<uint64_t>(
128         "xyz.openbmc_project.EntityManager", systemPath,
129         "xyz.openbmc_project.Inventory.Decorator.ManagedHost", "HostIndex",
130         [asyncResp, systemName, systemPath, callback = std::move(callback)](
131             const boost::system::error_code& ec, const uint64_t hostIndex) {
132             if (ec)
133             {
134                 BMCWEB_LOG_WARNING("DBUS response error {}", ec);
135                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
136                                            systemName);
137                 return;
138             }
139             BMCWEB_LOG_DEBUG("Got index {} for path {}", hostIndex, systemPath);
140             callback(hostIndex);
141         });
142 }
143 
144 inline void afterGetComputerSystemSubTreePaths(
145     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
146     const std::string& systemName,
147     std::function<void(const uint64_t computerSystemIndex)>& callback,
148     const boost::system::error_code& ec,
149     const dbus::utility::MapperGetSubTreePathsResponse& objects)
150 {
151     sdbusplus::message::object_path systemPath;
152     if (ec)
153     {
154         if (ec.value() == boost::system::errc::io_error)
155         {
156             BMCWEB_LOG_WARNING("EIO - System not found");
157             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
158                                        systemName);
159             return;
160         }
161 
162         BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value());
163         messages::internalError(asyncResp->res);
164         return;
165     }
166 
167     const auto& found = std::ranges::find_if(
168         objects, [systemName](const sdbusplus::message::object_path& path) {
169             return path.filename() == systemName;
170         });
171 
172     if (found == objects.end())
173     {
174         BMCWEB_LOG_WARNING("Failed to match systemName: {}", systemName);
175         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
176                                    systemName);
177         return;
178     }
179 
180     systemPath = *found;
181 
182     getManagedHostProperty(asyncResp, systemName, systemPath,
183                            std::move(callback));
184 }
185 
186 /**
187  * @brief Retrieve the index associated with the requested system based on the
188  * ManagedHost interface
189  *
190  * @param[i] asyncResp  Async response object
191  * @param[i] systemName The requested system
192  * @param[i] callback   Callback to call once the index has been found
193  *
194  * @return None
195  */
196 inline void getComputerSystemIndex(
197     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
198     const std::string& systemName,
199     std::function<void(const uint64_t computerSystemIndex)>&& callback)
200 {
201     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
202     {
203         constexpr std::array<std::string_view, 1> interfaces{
204             "xyz.openbmc_project.Inventory.Decorator.ManagedHost"};
205         dbus::utility::getSubTreePaths(
206             "/xyz/openbmc_project/inventory", 0, interfaces,
207             std::bind_front(afterGetComputerSystemSubTreePaths, asyncResp,
208                             systemName, std::move(callback)));
209     }
210     else
211     {
212         // on single-host, fallback to index 0
213         BMCWEB_LOG_DEBUG(
214             "Single-host detected, fallback to computerSystemIndex 0");
215         callback(0);
216     }
217 }
218 
219 inline sdbusplus::message::object_path getHostStateObjectPath(
220     const uint64_t computerSystemIndex)
221 {
222     const sdbusplus::message::object_path hostStatePath(
223         "/xyz/openbmc_project/state/host" +
224         std::to_string(computerSystemIndex));
225 
226     return hostStatePath;
227 }
228 
229 inline std::string getHostStateServiceName(const uint64_t computerSystemIndex)
230 {
231     std::string hostStateService = "xyz.openbmc_project.State.Host";
232     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
233     {
234         hostStateService += std::to_string(computerSystemIndex);
235     }
236 
237     return hostStateService;
238 }
239 
240 inline sdbusplus::message::object_path getChassisStateObjectPath(
241     const uint64_t computerSystemIndex)
242 {
243     const sdbusplus::message::object_path chassisStatePath(
244         "/xyz/openbmc_project/state/chassis" +
245         std::to_string(computerSystemIndex));
246 
247     return chassisStatePath;
248 }
249 
250 inline std::string getChassisStateServiceName(
251     const uint64_t computerSystemIndex)
252 {
253     std::string chassisStateService = "xyz.openbmc_project.State.Chassis";
254     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
255     {
256         chassisStateService += std::to_string(computerSystemIndex);
257     }
258 
259     return chassisStateService;
260 }
261 } // namespace redfish
262