1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 #pragma once 4 5 #include "app.hpp" 6 #include "async_resp.hpp" 7 #include "dbus_singleton.hpp" 8 #include "dbus_utility.hpp" 9 #include "error_messages.hpp" 10 #include "generated/enums/resource.hpp" 11 #include "http_request.hpp" 12 #include "http_response.hpp" 13 #include "logging.hpp" 14 #include "query.hpp" 15 #include "registries/privilege_registry.hpp" 16 #include "utils/collection.hpp" 17 #include "utils/dbus_utils.hpp" 18 19 #include <asm-generic/errno.h> 20 21 #include <boost/beast/http/verb.hpp> 22 #include <boost/system/error_code.hpp> 23 #include <boost/url/format.hpp> 24 #include <boost/url/url.hpp> 25 #include <sdbusplus/message/native_types.hpp> 26 #include <sdbusplus/unpack_properties.hpp> 27 28 #include <array> 29 #include <cmath> 30 #include <functional> 31 #include <memory> 32 #include <string> 33 #include <string_view> 34 35 namespace redfish 36 { 37 38 constexpr std::array<std::string_view, 1> cableInterfaces = { 39 "xyz.openbmc_project.Inventory.Item.Cable"}; 40 41 /** 42 * @brief Fill cable specific properties. 43 * @param[in,out] asyncResp Async HTTP response. 44 * @param[in] ec Error code corresponding to Async method call. 45 * @param[in] properties List of Cable Properties key/value pairs. 46 */ 47 inline void fillCableProperties( 48 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 49 const boost::system::error_code& ec, 50 const dbus::utility::DBusPropertiesMap& properties) 51 { 52 if (ec) 53 { 54 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 55 messages::internalError(asyncResp->res); 56 return; 57 } 58 59 const std::string* cableTypeDescription = nullptr; 60 const double* length = nullptr; 61 62 const bool success = sdbusplus::unpackPropertiesNoThrow( 63 dbus_utils::UnpackErrorPrinter(), properties, "CableTypeDescription", 64 cableTypeDescription, "Length", length); 65 66 if (!success) 67 { 68 messages::internalError(asyncResp->res); 69 return; 70 } 71 72 if (cableTypeDescription != nullptr) 73 { 74 asyncResp->res.jsonValue["CableType"] = *cableTypeDescription; 75 } 76 77 if (length != nullptr) 78 { 79 if (!std::isfinite(*length)) 80 { 81 // Cable length is NaN by default, do not throw an error 82 if (!std::isnan(*length)) 83 { 84 BMCWEB_LOG_ERROR("Cable length value is invalid"); 85 messages::internalError(asyncResp->res); 86 return; 87 } 88 } 89 else 90 { 91 asyncResp->res.jsonValue["LengthMeters"] = *length; 92 } 93 } 94 } 95 96 inline void fillCableHealthState( 97 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 98 const std::string& cableObjectPath, const std::string& service) 99 { 100 dbus::utility::getProperty<bool>( 101 *crow::connections::systemBus, service, cableObjectPath, 102 "xyz.openbmc_project.Inventory.Item", "Present", 103 [asyncResp, 104 cableObjectPath](const boost::system::error_code& ec, bool present) { 105 if (ec) 106 { 107 if (ec.value() != EBADR) 108 { 109 BMCWEB_LOG_ERROR( 110 "get presence failed for Cable {} with error {}", 111 cableObjectPath, ec.value()); 112 messages::internalError(asyncResp->res); 113 } 114 return; 115 } 116 117 if (!present) 118 { 119 asyncResp->res.jsonValue["Status"]["State"] = 120 resource::State::Absent; 121 } 122 }); 123 } 124 125 /** 126 * @brief Api to get Cable properties. 127 * @param[in,out] asyncResp Async HTTP response. 128 * @param[in] cableObjectPath Object path of the Cable. 129 * @param[in] serviceMap A map to hold Service and corresponding 130 * interface list for the given cable id. 131 */ 132 inline void getCableProperties( 133 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 134 const std::string& cableObjectPath, 135 const dbus::utility::MapperServiceMap& serviceMap) 136 { 137 BMCWEB_LOG_DEBUG("Get Properties for cable {}", cableObjectPath); 138 139 for (const auto& [service, interfaces] : serviceMap) 140 { 141 for (const auto& interface : interfaces) 142 { 143 if (interface == "xyz.openbmc_project.Inventory.Item.Cable") 144 { 145 dbus::utility::getAllProperties( 146 *crow::connections::systemBus, service, cableObjectPath, 147 interface, std::bind_front(fillCableProperties, asyncResp)); 148 } 149 else if (interface == "xyz.openbmc_project.Inventory.Item") 150 { 151 fillCableHealthState(asyncResp, cableObjectPath, service); 152 } 153 } 154 } 155 } 156 157 inline void afterHandleCableGet( 158 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 159 const std::string& cableId, const boost::system::error_code& ec, 160 const dbus::utility::MapperGetSubTreeResponse& subtree) 161 { 162 if (ec.value() == EBADR) 163 { 164 messages::resourceNotFound(asyncResp->res, "Cable", cableId); 165 return; 166 } 167 168 if (ec) 169 { 170 BMCWEB_LOG_ERROR("DBUS response error {}", ec.value()); 171 messages::internalError(asyncResp->res); 172 return; 173 } 174 175 for (const auto& [objectPath, serviceMap] : subtree) 176 { 177 sdbusplus::message::object_path path(objectPath); 178 if (path.filename() != cableId) 179 { 180 continue; 181 } 182 183 asyncResp->res.jsonValue["@odata.type"] = "#Cable.v1_0_0.Cable"; 184 asyncResp->res.jsonValue["@odata.id"] = 185 boost::urls::format("/redfish/v1/Cables/{}", cableId); 186 asyncResp->res.jsonValue["Id"] = cableId; 187 asyncResp->res.jsonValue["Name"] = "Cable"; 188 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; 189 190 getCableProperties(asyncResp, objectPath, serviceMap); 191 return; 192 } 193 messages::resourceNotFound(asyncResp->res, "Cable", cableId); 194 } 195 196 inline void handleCableGet(App& app, const crow::Request& req, 197 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 198 const std::string& cableId) 199 { 200 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 201 { 202 return; 203 } 204 205 BMCWEB_LOG_DEBUG("Cable Id: {}", cableId); 206 207 dbus::utility::getSubTree( 208 "/xyz/openbmc_project/inventory", 0, cableInterfaces, 209 std::bind_front(afterHandleCableGet, asyncResp, cableId)); 210 } 211 212 inline void handleCableCollectionGet( 213 App& app, const crow::Request& req, 214 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 215 { 216 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 217 { 218 return; 219 } 220 221 asyncResp->res.jsonValue["@odata.type"] = 222 "#CableCollection.CableCollection"; 223 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Cables"; 224 asyncResp->res.jsonValue["Name"] = "Cable Collection"; 225 asyncResp->res.jsonValue["Description"] = "Collection of Cable Entries"; 226 collection_util::getCollectionMembers( 227 asyncResp, boost::urls::url("/redfish/v1/Cables"), cableInterfaces, 228 "/xyz/openbmc_project/inventory"); 229 } 230 231 /** 232 * The Cable schema 233 */ 234 inline void requestRoutesCable(App& app) 235 { 236 BMCWEB_ROUTE(app, "/redfish/v1/Cables/<str>/") 237 .privileges(redfish::privileges::getCable) 238 .methods(boost::beast::http::verb::get)( 239 std::bind_front(handleCableGet, std::ref(app))); 240 } 241 242 /** 243 * Collection of Cable resource instances 244 */ 245 inline void requestRoutesCableCollection(App& app) 246 { 247 BMCWEB_ROUTE(app, "/redfish/v1/Cables/") 248 .privileges(redfish::privileges::getCableCollection) 249 .methods(boost::beast::http::verb::get)( 250 std::bind_front(handleCableCollectionGet, std::ref(app))); 251 } 252 253 } // namespace redfish 254