xref: /openbmc/bmcweb/redfish-core/lib/cable.hpp (revision 504af5a0568171b72caf13234cc81380b261fa21)
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  */
fillCableProperties(crow::Response & resp,const boost::system::error_code & ec,const dbus::utility::DBusPropertiesMap & properties)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  */
getCableProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & cableObjectPath,const dbus::utility::MapperServiceMap & serviceMap)96 inline void getCableProperties(
97     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  */
requestRoutesCable(App & app)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  */
requestRoutesCableCollection(App & app)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