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