xref: /openbmc/bmcweb/redfish-core/lib/cable.hpp (revision ab8cbe452946895b39620ff09a4df9eb32519b4d)
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  */
fillCableProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const dbus::utility::DBusPropertiesMap & properties)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 
fillCableHealthState(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & cableObjectPath,const std::string & service)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  */
getCableProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & cableObjectPath,const dbus::utility::MapperServiceMap & serviceMap)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 
afterHandleCableGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & cableId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)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 
handleCableGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & cableId)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 
handleCableCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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  */
requestRoutesCable(App & app)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  */
requestRoutesCableCollection(App & app)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