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