xref: /openbmc/bmcweb/redfish-core/lib/chassis.hpp (revision 7d95f5f6db1df37fe6438aa0e87479275c82807a)
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 using ManagedObjectsType = std::vector<
28     std::pair<dbus::object_path,
29               std::vector<std::pair<
30                   std::string,
31                   std::vector<std::pair<std::string, dbus::dbus_variant>>>>>>;
32 
33 using PropertiesType =
34     boost::container::flat_map<std::string, dbus::dbus_variant>;
35 
36 /**
37  * OnDemandChassisProvider
38  * Chassis provider class that retrieves data directly from dbus, before seting
39  * it into JSON output. This does not cache any data.
40  *
41  * Class can be a good example on how to scale different data providing
42  * solutions to produce single schema output.
43  *
44  * TODO(Pawel)
45  * This perhaps shall be different file, which has to be chosen on compile time
46  * depending on OEM needs
47  */
48 class OnDemandChassisProvider {
49  public:
50   /**
51    * Function that retrieves all properties for given Chassis Object from
52    * EntityManager
53    * @param res_name a chassis resource name to query on DBus
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_data(const std::string &res_name, CallbackFunc &&callback) {
59     crow::connections::system_bus->async_method_call(
60         [callback{std::move(callback)}](
61             const boost::system::error_code error_code,
62             const PropertiesType &properties) {
63           // Callback requires flat_map<string, string> so prepare one.
64           boost::container::flat_map<std::string, std::string> output;
65           if (error_code) {
66             // Something wrong on DBus, the error_code is not important at this
67             // moment, just return success=false, and empty output. Since size
68             // of map may vary depending on Chassis type, an empty output could
69             // not be treated same way as error.
70             callback(false, output);
71             return;
72           }
73           for (const std::pair<const char *, const char *> &p :
74                std::array<std::pair<const char *, const char *>, 5>{
75                    {{"name", "Name"},
76                     {"manufacturer", "Manufacturer"},
77                     {"model", "Model"},
78                     {"part_number", "PartNumber"},
79                     {"serial_number", "SerialNumber"}}}) {
80             PropertiesType::const_iterator it = properties.find(p.first);
81             if (it != properties.end()) {
82               const std::string *s = boost::get<std::string>(&it->second);
83               if (s != nullptr) {
84                 output[p.second] = *s;
85               }
86             }
87           }
88           // Callback with success, and hopefully data.
89           callback(true, output);
90         },
91         {"xyz.openbmc_project.EntityManager",
92          "/xyz/openbmc_project/Inventory/Item/Chassis/" + res_name,
93          "org.freedesktop.DBus.Properties", "GetAll"},
94         "xyz.openbmc_project.Configuration.Chassis");
95   }
96 
97   /**
98    * Function that retrieves all Chassis available through EntityManager.
99    * @param callback a function that shall be called to convert Dbus output into
100    * JSON.
101    */
102   template <typename CallbackFunc>
103   void get_chassis_list(CallbackFunc &&callback) {
104     crow::connections::system_bus->async_method_call(
105         [callback{std::move(callback)}](
106             const boost::system::error_code error_code,
107             const ManagedObjectsType &resp) {
108           // Callback requires vector<string> to retrieve all available chassis
109           // list.
110           std::vector<std::string> chassis_list;
111           if (error_code) {
112             // Something wrong on DBus, the error_code is not important at this
113             // moment, just return success=false, and empty output. Since size
114             // of vector may vary depending on information from Entity Manager,
115             // and empty output could not be treated same way as error.
116             callback(false, chassis_list);
117             return;
118           }
119 
120           // Iterate over all retrieved ObjectPaths.
121           for (auto &objpath : resp) {
122             // And all interfaces available for certain ObjectPath.
123             for (auto &interface : objpath.second) {
124               // If interface is xyz.openbmc_project.Configuration.Chassis, this
125               // is Chassis.
126               if (interface.first ==
127                   "xyz.openbmc_project.Configuration.Chassis") {
128                 // Cut out everyting until last "/", ...
129                 const std::string &chassis_id = objpath.first.value;
130                 std::size_t last_pos = chassis_id.rfind("/");
131                 if (last_pos != std::string::npos) {
132                   // and put it into output vector.
133                   chassis_list.emplace_back(chassis_id.substr(last_pos + 1));
134                 }
135               }
136             }
137           }
138           // Finally make a callback with usefull data
139           callback(true, chassis_list);
140         },
141         {"xyz.openbmc_project.EntityManager",
142          "/xyz/openbmc_project/Inventory/Item/Chassis",
143          "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"});
144   };
145 };
146 
147 /**
148  * ChassisCollection derived class for delivering Chassis Collection Schema
149  */
150 class ChassisCollection : public Node {
151  public:
152   template <typename CrowApp>
153   ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") {
154     Node::json["@odata.type"] = "#ChassisCollection.ChassisCollection";
155     Node::json["@odata.id"] = "/redfish/v1/Chassis";
156     Node::json["@odata.context"] =
157         "/redfish/v1/$metadata#ChassisCollection.ChassisCollection";
158     Node::json["Name"] = "Chassis Collection";
159 
160     entityPrivileges = {{crow::HTTPMethod::GET, {{"Login"}}},
161                         {crow::HTTPMethod::HEAD, {{"Login"}}},
162                         {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}},
163                         {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}},
164                         {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}},
165                         {crow::HTTPMethod::POST, {{"ConfigureComponents"}}}};
166   }
167 
168  private:
169   /**
170    * Functions triggers appropriate requests on DBus
171    */
172   void doGet(crow::response &res, const crow::request &req,
173              const std::vector<std::string> &params) override {
174     // Get chassis list, and call the below callback for JSON preparation
175     chassis_provider.get_chassis_list(
176         [&](const bool &success, const std::vector<std::string> &output) {
177           if (success) {
178             // ... prepare json array with appropriate @odata.id links
179             nlohmann::json chassis_array = nlohmann::json::array();
180             for (const std::string &chassis_item : output) {
181               chassis_array.push_back(
182                   {{"@odata.id", "/redfish/v1/Chassis/" + chassis_item}});
183             }
184             // Then attach members, count size and return,
185             Node::json["Members"] = chassis_array;
186             Node::json["Members@odata.count"] = chassis_array.size();
187             res.json_value = Node::json;
188           } else {
189             // ... otherwise, return INTERNALL ERROR
190             res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR);
191           }
192           res.end();
193         });
194   }
195 
196   // Chassis Provider object
197   // TODO(Pawel) consider move it to singleton
198   OnDemandChassisProvider chassis_provider;
199 };
200 
201 /**
202  * Chassis override class for delivering Chassis Schema
203  */
204 class Chassis : public Node {
205  public:
206   /*
207    * Default Constructor
208    */
209   template <typename CrowApp>
210   Chassis(CrowApp &app)
211       : Node(app, "/redfish/v1/Chassis/<str>/", std::string()) {
212     Node::json["@odata.type"] = "#Chassis.v1_4_0.Chassis";
213     Node::json["@odata.id"] = "/redfish/v1/Chassis";
214     Node::json["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis";
215     Node::json["Name"] = "Chassis Collection";
216     Node::json["ChassisType"] = "RackMount";
217 
218     entityPrivileges = {{crow::HTTPMethod::GET, {{"Login"}}},
219                         {crow::HTTPMethod::HEAD, {{"Login"}}},
220                         {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}},
221                         {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}},
222                         {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}},
223                         {crow::HTTPMethod::POST, {{"ConfigureComponents"}}}};
224   }
225 
226  private:
227   /**
228    * Functions triggers appropriate requests on DBus
229    */
230   void doGet(crow::response &res, const crow::request &req,
231              const std::vector<std::string> &params) override {
232     // Check if there is required param, truly entering this shall be
233     // impossible.
234     if (params.size() != 1) {
235       res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR);
236       res.end();
237       return;
238     }
239     const std::string &chassis_id = params[0];
240     // Get certain Chassis Data, and call the below callback for JSON
241     // preparation lambda requires everything as reference, and chassis_id,
242     // which is local by copy
243     chassis_provider.get_chassis_data(
244         chassis_id,
245         [&, chassis_id](const bool &success,
246                         const boost::container::flat_map<std::string,
247                                                          std::string> &output) {
248           // Create JSON copy based on Node::json, this is to avoid possible
249           // race condition
250           nlohmann::json json_response(Node::json);
251           // If success...
252           if (success) {
253             // prepare all the schema required fields.
254             json_response["@odata.id"] = "/redfish/v1/Chassis/" + chassis_id;
255             // also the one from dbus
256             for (const std::pair<std::string, std::string> &chassis_item :
257                  output) {
258               json_response[chassis_item.first] = chassis_item.second;
259             }
260 
261             json_response["Id"] = chassis_id;
262             // prepare respond, and send
263             res.json_value = json_response;
264           } else {
265             // ... otherwise return error
266             // TODO(Pawel)consider distinguish between non existing object, and
267             // other errors
268             res.code = static_cast<int>(HttpRespCode::NOT_FOUND);
269           }
270           res.end();
271         });
272   }
273 
274   // Chassis Provider object
275   // TODO(Pawel) consider move it to singleton
276   OnDemandChassisProvider chassis_provider;
277 };
278 
279 }  // namespace redfish
280