xref: /openbmc/bmcweb/features/redfish/lib/chassis.hpp (revision 603a664025801fd02079298374781f49ce816e5b)
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 
20 #include <boost/container/flat_map.hpp>
21 #include <variant>
22 
23 namespace redfish
24 {
25 
26 /**
27  * DBus types primitives for several generic DBus interfaces
28  * TODO(Pawel) consider move this to separate file into boost::dbus
29  */
30 // Note, this is not a very useful Variant, but because it isn't used to get
31 // values, it should be as simple as possible
32 // TODO(ed) invent a nullvariant type
33 using VariantType = std::variant<bool, std::string, uint64_t>;
34 using ManagedObjectsType = std::vector<std::pair<
35     sdbusplus::message::object_path,
36     std::vector<std::pair<std::string,
37                           std::vector<std::pair<std::string, VariantType>>>>>>;
38 
39 using PropertiesType = boost::container::flat_map<std::string, VariantType>;
40 
41 void getIntrusionByService(std::shared_ptr<AsyncResp> aResp,
42                            const std::string &service,
43                            const std::string &objPath)
44 {
45     BMCWEB_LOG_DEBUG << "Get intrusion status by service \n";
46 
47     crow::connections::systemBus->async_method_call(
48         [aResp{std::move(aResp)}](const boost::system::error_code ec,
49                                   const std::variant<std::string> &value) {
50             if (ec)
51             {
52                 // do not add err msg in redfish response, becaues this is not
53                 //     mandatory property
54                 BMCWEB_LOG_ERROR << "DBUS response error " << ec << "\n";
55                 return;
56             }
57 
58             const std::string *status = std::get_if<std::string>(&value);
59 
60             if (status == nullptr)
61             {
62                 BMCWEB_LOG_ERROR << "intrusion status read error \n";
63                 return;
64             }
65 
66             aResp->res.jsonValue["PhysicalSecurity"] = {
67                 {"IntrusionSensorNumber", 1}, {"IntrusionSensor", *status}};
68         },
69         service, objPath, "org.freedesktop.DBus.Properties", "Get",
70         "xyz.openbmc_project.Chassis.Intrusion", "Status");
71 }
72 
73 /**
74  * Retrieves physical security properties over dbus
75  */
76 void getPhysicalSecurityData(std::shared_ptr<AsyncResp> aResp)
77 {
78     crow::connections::systemBus->async_method_call(
79         [aResp{std::move(aResp)}](
80             const boost::system::error_code ec,
81             const std::vector<std::pair<
82                 std::string,
83                 std::vector<std::pair<std::string, std::vector<std::string>>>>>
84                 &subtree) {
85             if (ec)
86             {
87                 // do not add err msg in redfish response, becaues this is not
88                 //     mandatory property
89                 BMCWEB_LOG_ERROR << "DBUS error: no matched iface " << ec
90                                  << "\n";
91                 return;
92             }
93             // Iterate over all retrieved ObjectPaths.
94             for (const auto &object : subtree)
95             {
96                 for (const auto &service : object.second)
97                 {
98                     getIntrusionByService(aResp, service.first, object.first);
99                     return;
100                 }
101             }
102         },
103         "xyz.openbmc_project.ObjectMapper",
104         "/xyz/openbmc_project/object_mapper",
105         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
106         "/xyz/openbmc_project/Intrusion", int32_t(1),
107         std::array<const char *, 1>{"xyz.openbmc_project.Chassis.Intrusion"});
108 }
109 
110 /**
111  * ChassisCollection derived class for delivering Chassis Collection Schema
112  */
113 class ChassisCollection : public Node
114 {
115   public:
116     ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/")
117     {
118         entityPrivileges = {
119             {boost::beast::http::verb::get, {{"Login"}}},
120             {boost::beast::http::verb::head, {{"Login"}}},
121             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
122             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
123             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
124             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
125     }
126 
127   private:
128     /**
129      * Functions triggers appropriate requests on DBus
130      */
131     void doGet(crow::Response &res, const crow::Request &req,
132                const std::vector<std::string> &params) override
133     {
134         res.jsonValue["@odata.type"] = "#ChassisCollection.ChassisCollection";
135         res.jsonValue["@odata.id"] = "/redfish/v1/Chassis";
136         res.jsonValue["@odata.context"] =
137             "/redfish/v1/$metadata#ChassisCollection.ChassisCollection";
138         res.jsonValue["Name"] = "Chassis Collection";
139 
140 #ifdef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS
141         // Assume one Chassis named "chassis"
142         res.jsonValue["Members@odata.count"] = 1;
143         res.jsonValue["Members"] = {
144             {{"@odata.id", "/redfish/v1/Chassis/chassis"}}};
145         res.end();
146         return;
147 #endif
148         const std::array<const char *, 3> interfaces = {
149             "xyz.openbmc_project.Inventory.Item.Board",
150             "xyz.openbmc_project.Inventory.Item.Chassis",
151             "xyz.openbmc_project.Inventory.Item.PowerSupply"};
152 
153         auto asyncResp = std::make_shared<AsyncResp>(res);
154         crow::connections::systemBus->async_method_call(
155             [asyncResp](const boost::system::error_code ec,
156                         const std::vector<std::string> &chassisList) {
157                 if (ec)
158                 {
159                     messages::internalError(asyncResp->res);
160                     return;
161                 }
162                 nlohmann::json &chassisArray =
163                     asyncResp->res.jsonValue["Members"];
164                 chassisArray = nlohmann::json::array();
165                 for (const std::string &objpath : chassisList)
166                 {
167                     std::size_t lastPos = objpath.rfind("/");
168                     if (lastPos == std::string::npos)
169                     {
170                         BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath;
171                         continue;
172                     }
173                     chassisArray.push_back(
174                         {{"@odata.id", "/redfish/v1/Chassis/" +
175                                            objpath.substr(lastPos + 1)}});
176                 }
177 
178                 asyncResp->res.jsonValue["Members@odata.count"] =
179                     chassisArray.size();
180             },
181             "xyz.openbmc_project.ObjectMapper",
182             "/xyz/openbmc_project/object_mapper",
183             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
184             "/xyz/openbmc_project/inventory", int32_t(0), interfaces);
185     }
186 };
187 
188 /**
189  * Chassis override class for delivering Chassis Schema
190  */
191 class Chassis : public Node
192 {
193   public:
194     Chassis(CrowApp &app) :
195         Node(app, "/redfish/v1/Chassis/<str>/", std::string())
196     {
197         entityPrivileges = {
198             {boost::beast::http::verb::get, {{"Login"}}},
199             {boost::beast::http::verb::head, {{"Login"}}},
200             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
201             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
202             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
203             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
204     }
205 
206   private:
207     /**
208      * Functions triggers appropriate requests on DBus
209      */
210     void doGet(crow::Response &res, const crow::Request &req,
211                const std::vector<std::string> &params) override
212     {
213         const std::array<const char *, 3> interfaces = {
214             "xyz.openbmc_project.Inventory.Item.Board",
215             "xyz.openbmc_project.Inventory.Item.Chassis",
216             "xyz.openbmc_project.Inventory.Item.PowerSupply"};
217 
218         // Check if there is required param, truly entering this shall be
219         // impossible.
220         if (params.size() != 1)
221         {
222             messages::internalError(res);
223             res.end();
224             return;
225         }
226         const std::string &chassisId = params[0];
227 #ifdef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS
228         // In a one chassis system the only supported name is "chassis"
229         if (chassisId != "chassis")
230         {
231             messages::resourceNotFound(res, "#Chassis.v1_4_0.Chassis",
232                                        chassisId);
233             res.end();
234             return;
235         }
236 #endif
237 
238         res.jsonValue["@odata.type"] = "#Chassis.v1_4_0.Chassis";
239         res.jsonValue["@odata.id"] = "/redfish/v1/Chassis/" + chassisId;
240         res.jsonValue["@odata.context"] =
241             "/redfish/v1/$metadata#Chassis.Chassis";
242         res.jsonValue["Name"] = "Chassis Collection";
243         res.jsonValue["ChassisType"] = "RackMount";
244         res.jsonValue["PowerState"] = "On";
245 
246         auto asyncResp = std::make_shared<AsyncResp>(res);
247         crow::connections::systemBus->async_method_call(
248             [asyncResp, chassisId(std::string(chassisId))](
249                 const boost::system::error_code ec,
250                 const std::vector<std::pair<
251                     std::string, std::vector<std::pair<
252                                      std::string, std::vector<std::string>>>>>
253                     &subtree) {
254                 if (ec)
255                 {
256                     messages::internalError(asyncResp->res);
257                     return;
258                 }
259                 // Iterate over all retrieved ObjectPaths.
260                 for (const std::pair<
261                          std::string,
262                          std::vector<
263                              std::pair<std::string, std::vector<std::string>>>>
264                          &object : subtree)
265                 {
266                     const std::string &path = object.first;
267                     const std::vector<
268                         std::pair<std::string, std::vector<std::string>>>
269                         &connectionNames = object.second;
270 
271 // If only one chassis, just select the first one
272 #ifndef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS
273                     if (!boost::ends_with(path, chassisId))
274                     {
275                         continue;
276                     }
277 #endif
278                     if (connectionNames.size() < 1)
279                     {
280                         BMCWEB_LOG_ERROR << "Only got "
281                                          << connectionNames.size()
282                                          << " Connection names";
283                         continue;
284                     }
285 
286                     const std::string connectionName = connectionNames[0].first;
287                     crow::connections::systemBus->async_method_call(
288                         [asyncResp, chassisId(std::string(chassisId))](
289                             const boost::system::error_code ec,
290                             const std::vector<std::pair<
291                                 std::string, VariantType>> &propertiesList) {
292                             for (const std::pair<std::string, VariantType>
293                                      &property : propertiesList)
294                             {
295                                 // Store DBus properties that are also Redfish
296                                 // properties with same name and a string value
297                                 const std::string &propertyName =
298                                     property.first;
299                                 if ((propertyName == "PartNumber") ||
300                                     (propertyName == "SerialNumber") ||
301                                     (propertyName == "Manufacturer") ||
302                                     (propertyName == "Model"))
303                                 {
304                                     const std::string *value =
305                                         std::get_if<std::string>(
306                                             &property.second);
307                                     if (value != nullptr)
308                                     {
309                                         asyncResp->res.jsonValue[propertyName] =
310                                             *value;
311                                     }
312                                 }
313                             }
314                             asyncResp->res.jsonValue["Name"] = chassisId;
315                             asyncResp->res.jsonValue["Id"] = chassisId;
316                             asyncResp->res.jsonValue["Thermal"] = {
317                                 {"@odata.id", "/redfish/v1/Chassis/" +
318                                                   chassisId + "/Thermal"}};
319                             // Power object
320                             asyncResp->res.jsonValue["Power"] = {
321                                 {"@odata.id", "/redfish/v1/Chassis/" +
322                                                   chassisId + "/Power"}};
323                             asyncResp->res.jsonValue["Status"] = {
324                                 {"Health", "OK"},
325                                 {"State", "Enabled"},
326                             };
327 
328                             asyncResp->res
329                                 .jsonValue["Links"]["ComputerSystems"] = {
330                                 {{"@odata.id", "/redfish/v1/Systems/system"}}};
331                             asyncResp->res.jsonValue["Links"]["ManagedBy"] = {
332                                 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
333                         },
334                         connectionName, path, "org.freedesktop.DBus.Properties",
335                         "GetAll",
336                         "xyz.openbmc_project.Inventory.Decorator.Asset");
337                     return;
338                 }
339 
340                 // Couldn't find an object with that name.  return an error
341                 messages::resourceNotFound(
342                     asyncResp->res, "#Chassis.v1_4_0.Chassis", chassisId);
343             },
344             "xyz.openbmc_project.ObjectMapper",
345             "/xyz/openbmc_project/object_mapper",
346             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
347             "/xyz/openbmc_project/inventory", int32_t(0), interfaces);
348 
349         getPhysicalSecurityData(asyncResp);
350     }
351 };
352 } // namespace redfish
353