xref: /openbmc/bmcweb/redfish-core/include/utils/systems_utils.hpp (revision 3577e44683a5ade8ad02a6418984b56f4ca2bcac)
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_utility.hpp"
9 #include "error_messages.hpp"
10 #include "human_sort.hpp"
11 #include "logging.hpp"
12 
13 #include <boost/url/format.hpp>
14 #include <sdbusplus/message/native_types.hpp>
15 
16 #include <algorithm>
17 #include <array>
18 #include <cstdint>
19 #include <functional>
20 #include <memory>
21 #include <string>
22 #include <string_view>
23 #include <utility>
24 #include <vector>
25 
26 namespace redfish
27 {
28 
handleSystemCollectionMembers(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreePathsResponse & objects)29 inline void handleSystemCollectionMembers(
30     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
31     const boost::system::error_code& ec,
32     const dbus::utility::MapperGetSubTreePathsResponse& objects)
33 {
34     if (ec == boost::system::errc::io_error)
35     {
36         asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
37         asyncResp->res.jsonValue["Members@odata.count"] = 0;
38         return;
39     }
40 
41     if (ec)
42     {
43         BMCWEB_LOG_ERROR("DBUS response error {}", ec.value());
44         messages::internalError(asyncResp->res);
45         return;
46     }
47 
48     nlohmann::json& membersArray = asyncResp->res.jsonValue["Members"];
49     membersArray = nlohmann::json::array();
50 
51     // consider an empty result as single-host, since single-host systems
52     // do not populate the ManagedHost dbus interface
53     if (objects.empty())
54     {
55         asyncResp->res.jsonValue["Members@odata.count"] = 1;
56         nlohmann::json::object_t system;
57         system["@odata.id"] = boost::urls::format(
58             "/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME);
59         membersArray.emplace_back(std::move(system));
60 
61         if constexpr (BMCWEB_HYPERVISOR_COMPUTER_SYSTEM)
62         {
63             BMCWEB_LOG_DEBUG("Hypervisor is available");
64             asyncResp->res.jsonValue["Members@odata.count"] = 2;
65 
66             nlohmann::json::object_t hypervisor;
67             hypervisor["@odata.id"] = "/redfish/v1/Systems/hypervisor";
68             membersArray.emplace_back(std::move(hypervisor));
69         }
70 
71         return;
72     }
73 
74     std::vector<std::string> pathNames;
75     for (const auto& object : objects)
76     {
77         sdbusplus::message::object_path path(object);
78         std::string leaf = path.filename();
79         if (leaf.empty())
80         {
81             continue;
82         }
83         pathNames.emplace_back(leaf);
84     }
85     std::ranges::sort(pathNames, AlphanumLess<std::string>());
86 
87     for (const std::string& systemName : pathNames)
88     {
89         nlohmann::json::object_t member;
90         member["@odata.id"] =
91             boost::urls::format("/redfish/v1/Systems/{}", systemName);
92         membersArray.emplace_back(std::move(member));
93     }
94     asyncResp->res.jsonValue["Members@odata.count"] = membersArray.size();
95 }
96 
97 /**
98  * @brief Populate the system collection members from a GetSubTreePaths search
99  * of the inventory based of the ManagedHost dbus interface
100  *
101  * @param[i] asyncResp  Async response object
102  *
103  * @return None
104  */
getSystemCollectionMembers(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)105 inline void getSystemCollectionMembers(
106     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
107 {
108     constexpr std::array<std::string_view, 1> interfaces{
109         "xyz.openbmc_project.Inventory.Decorator.ManagedHost",
110     };
111 
112     BMCWEB_LOG_DEBUG("Get system collection members for /redfish/v1/Systems");
113 
114     dbus::utility::getSubTreePaths(
115         "/xyz/openbmc_project/inventory", 0, interfaces,
116         std::bind_front(handleSystemCollectionMembers, asyncResp));
117 }
118 
getManagedHostProperty(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & systemPath,std::function<void (const uint64_t computerSystemIndex)> callback)119 inline void getManagedHostProperty(
120     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
121     const std::string& systemName, const std::string& systemPath,
122     std::function<void(const uint64_t computerSystemIndex)> callback)
123 {
124     dbus::utility::getProperty<uint64_t>(
125         "xyz.openbmc_project.EntityManager", systemPath,
126         "xyz.openbmc_project.Inventory.Decorator.ManagedHost", "HostIndex",
127         [asyncResp, systemName, systemPath, callback = std::move(callback)](
128             const boost::system::error_code& ec, const uint64_t hostIndex) {
129             if (ec)
130             {
131                 BMCWEB_LOG_WARNING("DBUS response error {}", ec);
132                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
133                                            systemName);
134                 return;
135             }
136             BMCWEB_LOG_DEBUG("Got index {} for path {}", hostIndex, systemPath);
137             callback(hostIndex);
138         });
139 }
140 
afterGetComputerSystemSubTreePaths(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,std::function<void (const uint64_t computerSystemIndex)> & callback,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreePathsResponse & objects)141 inline void afterGetComputerSystemSubTreePaths(
142     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
143     const std::string& systemName,
144     std::function<void(const uint64_t computerSystemIndex)>& callback,
145     const boost::system::error_code& ec,
146     const dbus::utility::MapperGetSubTreePathsResponse& objects)
147 {
148     sdbusplus::message::object_path systemPath;
149     if (ec)
150     {
151         if (ec.value() == boost::system::errc::io_error)
152         {
153             BMCWEB_LOG_WARNING("EIO - System not found");
154             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
155                                        systemName);
156             return;
157         }
158 
159         BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value());
160         messages::internalError(asyncResp->res);
161         return;
162     }
163 
164     const auto& found = std::ranges::find_if(
165         objects, [systemName](const sdbusplus::message::object_path& path) {
166             return path.filename() == systemName;
167         });
168 
169     if (found == objects.end())
170     {
171         BMCWEB_LOG_WARNING("Failed to match systemName: {}", systemName);
172         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
173                                    systemName);
174         return;
175     }
176 
177     systemPath = *found;
178 
179     getManagedHostProperty(asyncResp, systemName, systemPath,
180                            std::move(callback));
181 }
182 
183 /**
184  * @brief Retrieve the index associated with the requested system based on the
185  * ManagedHost interface
186  *
187  * @param[i] asyncResp  Async response object
188  * @param[i] systemName The requested system
189  * @param[i] callback   Callback to call once the index has been found
190  *
191  * @return None
192  */
getComputerSystemIndex(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,std::function<void (const uint64_t computerSystemIndex)> && callback)193 inline void getComputerSystemIndex(
194     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
195     const std::string& systemName,
196     std::function<void(const uint64_t computerSystemIndex)>&& callback)
197 {
198     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
199     {
200         constexpr std::array<std::string_view, 1> interfaces{
201             "xyz.openbmc_project.Inventory.Decorator.ManagedHost"};
202         dbus::utility::getSubTreePaths(
203             "/xyz/openbmc_project/inventory", 0, interfaces,
204             std::bind_front(afterGetComputerSystemSubTreePaths, asyncResp,
205                             systemName, std::move(callback)));
206     }
207     else
208     {
209         // on single-host, fallback to index 0
210         BMCWEB_LOG_DEBUG(
211             "Single-host detected, fallback to computerSystemIndex 0");
212         callback(0);
213     }
214 }
215 
getHostStateObjectPath(const uint64_t computerSystemIndex)216 inline sdbusplus::message::object_path getHostStateObjectPath(
217     const uint64_t computerSystemIndex)
218 {
219     const sdbusplus::message::object_path hostStatePath(
220         "/xyz/openbmc_project/state/host" +
221         std::to_string(computerSystemIndex));
222 
223     return hostStatePath;
224 }
225 
getHostStateServiceName(const uint64_t computerSystemIndex)226 inline std::string getHostStateServiceName(const uint64_t computerSystemIndex)
227 {
228     std::string hostStateService = "xyz.openbmc_project.State.Host";
229     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
230     {
231         hostStateService += std::to_string(computerSystemIndex);
232     }
233 
234     return hostStateService;
235 }
236 
getChassisStateObjectPath(const uint64_t computerSystemIndex)237 inline sdbusplus::message::object_path getChassisStateObjectPath(
238     const uint64_t computerSystemIndex)
239 {
240     const sdbusplus::message::object_path chassisStatePath(
241         "/xyz/openbmc_project/state/chassis" +
242         std::to_string(computerSystemIndex));
243 
244     return chassisStatePath;
245 }
246 
getChassisStateServiceName(const uint64_t computerSystemIndex)247 inline std::string getChassisStateServiceName(
248     const uint64_t computerSystemIndex)
249 {
250     std::string chassisStateService = "xyz.openbmc_project.State.Chassis";
251     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
252     {
253         chassisStateService += std::to_string(computerSystemIndex);
254     }
255 
256     return chassisStateService;
257 }
258 
259 namespace systems_utils
260 {
261 
afterGetValidSystemsPath(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemId,const std::function<void (const std::optional<std::string> &)> & callback,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreePathsResponse & systemsPaths)262 inline void afterGetValidSystemsPath(
263     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
264     const std::string& systemId,
265     const std::function<void(const std::optional<std::string>&)>& callback,
266     const boost::system::error_code& ec,
267     const dbus::utility::MapperGetSubTreePathsResponse& systemsPaths)
268 {
269     if (ec)
270     {
271         if (ec == boost::system::errc::io_error)
272         {
273             BMCWEB_LOG_DEBUG("No systems found");
274             callback(std::nullopt);
275             return;
276         }
277         BMCWEB_LOG_ERROR("DBUS response error: {}", ec.value());
278         messages::internalError(asyncResp->res);
279         return;
280     }
281 
282     for (const std::string& system : systemsPaths)
283     {
284         sdbusplus::message::object_path path(system);
285         if (path.filename() == systemId)
286         {
287             callback(path);
288             return;
289         }
290     }
291     BMCWEB_LOG_DEBUG("No system named {} found", systemId);
292     callback(std::nullopt);
293 }
294 
getValidSystemsPath(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemId,std::function<void (const std::optional<std::string> &)> && callback)295 inline void getValidSystemsPath(
296     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
297     const std::string& systemId,
298     std::function<void(const std::optional<std::string>&)>&& callback)
299 {
300     BMCWEB_LOG_DEBUG("Get path for {}", systemId);
301 
302     constexpr std::array<std::string_view, 2> interfaces = {
303         "xyz.openbmc_project.Inventory.Decorator.ManagedHost",
304         "xyz.openbmc_project.Inventory.Item.System"};
305     dbus::utility::getSubTreePaths(
306         "/xyz/openbmc_project/inventory", 0, interfaces,
307         [asyncResp, systemId, callback{std::move(callback)}](
308             const boost::system::error_code& ec,
309             const dbus::utility::MapperGetSubTreePathsResponse& systemsPaths) {
310             afterGetValidSystemsPath(asyncResp, systemId, callback, ec,
311                                      systemsPaths);
312         });
313 }
314 
315 } // namespace systems_utils
316 
317 /**
318  * @brief Match computerSystemIndex with index contained by an object path
319  *        i.e 1 in /xyz/openbmc/project/control/host1/policy/TPMEnable
320  *
321  * @param[i] asyncResp           Shared pointer for generating response
322  * @param[i] computerSystemIndex The index to match against
323  * @param[i] subtree             Mapper response object
324  * @param[o] objectPath          Buffer for matched object path
325  * @param[o] service             Buffer for service of matched object
326  *                               path
327  *
328  * @return true if match found, else false
329  */
indexMatchingSubTreeMapObjectPath(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const uint64_t computerSystemIndex,const dbus::utility::MapperGetSubTreeResponse & subtree,std::string & objectPath,std::string & service)330 inline bool indexMatchingSubTreeMapObjectPath(
331     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
332     const uint64_t computerSystemIndex,
333     const dbus::utility::MapperGetSubTreeResponse& subtree,
334     std::string& objectPath, std::string& service)
335 {
336     const std::string host = std::format("host{}", computerSystemIndex);
337 
338     for (const auto& obj : subtree)
339     {
340         std::string tmp = host;
341         const sdbusplus::message::object_path path{obj.first};
342         const std::string serv = obj.second.begin()->first;
343 
344         if (path.str.empty() || obj.second.size() != 1)
345         {
346             BMCWEB_LOG_DEBUG("Error finding index in object path");
347             messages::internalError(asyncResp->res);
348             return false;
349         }
350 
351         objectPath = path;
352         service = serv;
353 
354         if (path.filename() == host)
355         {
356             return true;
357         }
358 
359         tmp.insert(0, 1, '/');
360         tmp.append("/");
361         if (path.str.find(host) != std::string::npos)
362         {
363             return true;
364         }
365     }
366 
367     return false;
368 }
369 } // namespace redfish
370