xref: /openbmc/bmcweb/redfish-core/lib/chassis.hpp (revision 683f7276)
1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 
18 #include "node.hpp"
19 
20 #include <boost/container/flat_map.hpp>
21 
22 namespace redfish
23 {
24 
25 /**
26  * DBus types primitives for several generic DBus interfaces
27  * TODO(Pawel) consider move this to separate file into boost::dbus
28  */
29 // Note, this is not a very useful Variant, but because it isn't used to get
30 // values, it should be as simple as possible
31 // TODO(ed) invent a nullvariant type
32 using VariantType = sdbusplus::message::variant<bool, std::string>;
33 using ManagedObjectsType = std::vector<std::pair<
34     sdbusplus::message::object_path,
35     std::vector<std::pair<std::string,
36                           std::vector<std::pair<std::string, VariantType>>>>>>;
37 
38 using PropertiesType = boost::container::flat_map<std::string, VariantType>;
39 
40 /**
41  * OnDemandChassisProvider
42  * Chassis provider class that retrieves data directly from dbus, before setting
43  * it into JSON output. This does not cache any data.
44  *
45  * Class can be a good example on how to scale different data providing
46  * solutions to produce single schema output.
47  *
48  * TODO(Pawel)
49  * This perhaps shall be different file, which has to be chosen on compile time
50  * depending on OEM needs
51  */
52 class OnDemandChassisProvider
53 {
54   public:
55     /**
56      * Function that retrieves all Chassis available through EntityManager.
57      * @param callback a function that shall be called to convert Dbus output
58      * into JSON.
59      */
60     template <typename CallbackFunc>
61     void getChassisList(CallbackFunc &&callback)
62     {
63         const std::array<const char *, 4> interfaces = {
64             "xyz.openbmc_project.Inventory.Item.Board",
65             "xyz.openbmc_project.Inventory.Item.Chassis",
66             "xyz.openbmc_project.Inventory.Item.PowerSupply",
67             "xyz.openbmc_project.Inventory.Item.System",
68         };
69         crow::connections::systemBus->async_method_call(
70             [callback{std::move(callback)}](
71                 const boost::system::error_code error_code,
72                 const std::vector<std::string> &resp) {
73                 // Callback requires vector<string> to retrieve all available
74                 // chassis list.
75                 std::vector<std::string> chassisList;
76                 if (error_code)
77                 {
78                     // Something wrong on DBus, the error_code is not important
79                     // at this moment, just return success=false, and empty
80                     // output. Since size of vector may vary depending on
81                     // information from Entity Manager, and empty output could
82                     // not be treated same way as error.
83                     callback(false, chassisList);
84                     return;
85                 }
86                 // Iterate over all retrieved ObjectPaths.
87                 for (const std::string &objpath : resp)
88                 {
89                     std::size_t lastPos = objpath.rfind("/");
90                     if (lastPos != std::string::npos)
91                     {
92                         // and put it into output vector.
93                         chassisList.emplace_back(objpath.substr(lastPos + 1));
94                     }
95                 }
96                 // Finally make a callback with useful data
97                 callback(true, chassisList);
98             },
99             "xyz.openbmc_project.ObjectMapper",
100             "/xyz/openbmc_project/object_mapper",
101             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
102             "/xyz/openbmc_project/inventory", int32_t(3), interfaces);
103     };
104 };
105 
106 /**
107  * ChassisCollection derived class for delivering Chassis Collection Schema
108  */
109 class ChassisCollection : public Node
110 {
111   public:
112     ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/")
113     {
114         Node::json["@odata.type"] = "#ChassisCollection.ChassisCollection";
115         Node::json["@odata.id"] = "/redfish/v1/Chassis";
116         Node::json["@odata.context"] =
117             "/redfish/v1/$metadata#ChassisCollection.ChassisCollection";
118         Node::json["Name"] = "Chassis Collection";
119 
120         entityPrivileges = {
121             {boost::beast::http::verb::get, {{"Login"}}},
122             {boost::beast::http::verb::head, {{"Login"}}},
123             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
124             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
125             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
126             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
127     }
128 
129   private:
130     /**
131      * Functions triggers appropriate requests on DBus
132      */
133     void doGet(crow::Response &res, const crow::Request &req,
134                const std::vector<std::string> &params) override
135     {
136         // get chassis list, and call the below callback for JSON preparation
137         chassisProvider.getChassisList(
138             [&](const bool &success, const std::vector<std::string> &output) {
139                 if (success)
140                 {
141                     // ... prepare json array with appropriate @odata.id links
142                     nlohmann::json chassisArray = nlohmann::json::array();
143                     for (const std::string &chassisItem : output)
144                     {
145                         chassisArray.push_back(
146                             {{"@odata.id",
147                               "/redfish/v1/Chassis/" + chassisItem}});
148                     }
149                     // Then attach members, count size and return,
150                     Node::json["Members"] = chassisArray;
151                     Node::json["Members@odata.count"] = chassisArray.size();
152                     res.jsonValue = Node::json;
153                 }
154                 else
155                 {
156                     // ... otherwise, return INTERNALL ERROR
157                     res.result(
158                         boost::beast::http::status::internal_server_error);
159                 }
160                 res.end();
161             });
162     }
163 
164     // Chassis Provider object
165     // TODO(Pawel) consider move it to singleton
166     OnDemandChassisProvider chassisProvider;
167 };
168 
169 /**
170  * Chassis override class for delivering Chassis Schema
171  */
172 class Chassis : public Node
173 {
174   public:
175     Chassis(CrowApp &app) :
176         Node(app, "/redfish/v1/Chassis/<str>/", std::string())
177     {
178         Node::json["@odata.type"] = "#Chassis.v1_4_0.Chassis";
179         Node::json["@odata.id"] = "/redfish/v1/Chassis";
180         Node::json["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis";
181         Node::json["Name"] = "Chassis Collection";
182         Node::json["ChassisType"] = "RackMount";
183 
184         entityPrivileges = {
185             {boost::beast::http::verb::get, {{"Login"}}},
186             {boost::beast::http::verb::head, {{"Login"}}},
187             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
188             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
189             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
190             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
191     }
192 
193   private:
194     /**
195      * Functions triggers appropriate requests on DBus
196      */
197     void doGet(crow::Response &res, const crow::Request &req,
198                const std::vector<std::string> &params) override
199     {
200         // Check if there is required param, truly entering this shall be
201         // impossible.
202         if (params.size() != 1)
203         {
204             res.result(boost::beast::http::status::internal_server_error);
205             res.end();
206             return;
207         }
208 
209         res.jsonValue = Node::json;
210         const std::string &chassisId = params[0];
211         crow::connections::systemBus->async_method_call(
212             [&res, chassisId(std::string(chassisId))](
213                 const boost::system::error_code error_code,
214                 const std::vector<std::pair<
215                     std::string, std::vector<std::pair<
216                                      std::string, std::vector<std::string>>>>>
217                     &subtree) {
218                 if (error_code)
219                 {
220                     res.jsonValue = {};
221                     res.result(
222                         boost::beast::http::status::internal_server_error);
223                     res.end();
224                     return;
225                 }
226                 // Iterate over all retrieved ObjectPaths.
227                 for (const std::pair<
228                          std::string,
229                          std::vector<
230                              std::pair<std::string, std::vector<std::string>>>>
231                          &object : subtree)
232                 {
233                     const std::string &path = object.first;
234                     const std::vector<
235                         std::pair<std::string, std::vector<std::string>>>
236                         &connectionNames = object.second;
237 
238                     if (!boost::ends_with(path, chassisId))
239                     {
240                         continue;
241                     }
242                     if (connectionNames.size() < 1)
243                     {
244                         BMCWEB_LOG_ERROR << "Only got "
245                                          << connectionNames.size()
246                                          << " Connection names";
247                         continue;
248                     }
249 
250                     const std::string connectionName = connectionNames[0].first;
251                     crow::connections::systemBus->async_method_call(
252                         [&res, chassisId(std::string(chassisId))](
253                             const boost::system::error_code error_code,
254                             const std::vector<std::pair<
255                                 std::string, VariantType>> &propertiesList) {
256                             for (const std::pair<std::string, VariantType>
257                                      &property : propertiesList)
258                             {
259                                 const std::string *value =
260                                     mapbox::getPtr<const std::string>(
261                                         property.second);
262                                 if (value != nullptr)
263                                 {
264                                     res.jsonValue[property.first] = *value;
265                                 }
266                             }
267                             res.jsonValue["Name"] = chassisId;
268                             res.jsonValue["Id"] = chassisId;
269                             res.jsonValue["Thermal"] = {
270                                 {"@odata.id", "/redfish/v1/Chassis/" +
271                                                   chassisId + "/Thermal"}};
272                             res.end();
273                         },
274                         connectionName, path, "org.freedesktop.DBus.Properties",
275                         "GetAll",
276                         "xyz.openbmc_project.Inventory.Decorator.Asset");
277                     // Found the Connection we were looking for, return
278                     return;
279                 }
280 
281                 // Couldn't find an object with that name.  return an error
282                 res.result(boost::beast::http::status::not_found);
283 
284                 res.end();
285             },
286             "xyz.openbmc_project.ObjectMapper",
287             "/xyz/openbmc_project/object_mapper",
288             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
289             "/xyz/openbmc_project/inventory", int32_t(0),
290             std::array<const char *, 1>{
291                 "xyz.openbmc_project.Inventory.Decorator.Asset"});
292     }
293 
294     // Chassis Provider object
295     // TODO(Pawel) consider move it to singleton
296     OnDemandChassisProvider chassisProvider;
297 }; // namespace redfish
298 
299 } // namespace redfish
300