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 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 */ 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 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 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 */ 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 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 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 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 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 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 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 */ 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