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