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