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