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