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