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