xref: /openbmc/bmcweb/features/redfish/lib/chassis.hpp (revision c181942f49d754983981f9dc24f0129270ab02eb)
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         const std::array<const char *, 3> interfaces = {
135             "xyz.openbmc_project.Inventory.Item.Board",
136             "xyz.openbmc_project.Inventory.Item.Chassis",
137             "xyz.openbmc_project.Inventory.Item.PowerSupply"};
138         res.jsonValue["@odata.type"] = "#ChassisCollection.ChassisCollection";
139         res.jsonValue["@odata.id"] = "/redfish/v1/Chassis";
140         res.jsonValue["@odata.context"] =
141             "/redfish/v1/$metadata#ChassisCollection.ChassisCollection";
142         res.jsonValue["Name"] = "Chassis Collection";
143 
144         auto asyncResp = std::make_shared<AsyncResp>(res);
145         crow::connections::systemBus->async_method_call(
146             [asyncResp](const boost::system::error_code ec,
147                         const std::vector<std::string> &chassisList) {
148                 if (ec)
149                 {
150                     messages::internalError(asyncResp->res);
151                     return;
152                 }
153                 nlohmann::json &chassisArray =
154                     asyncResp->res.jsonValue["Members"];
155                 chassisArray = nlohmann::json::array();
156                 for (const std::string &objpath : chassisList)
157                 {
158                     std::size_t lastPos = objpath.rfind("/");
159                     if (lastPos == std::string::npos)
160                     {
161                         BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath;
162                         continue;
163                     }
164                     chassisArray.push_back(
165                         {{"@odata.id", "/redfish/v1/Chassis/" +
166                                            objpath.substr(lastPos + 1)}});
167                 }
168 
169                 asyncResp->res.jsonValue["Members@odata.count"] =
170                     chassisArray.size();
171             },
172             "xyz.openbmc_project.ObjectMapper",
173             "/xyz/openbmc_project/object_mapper",
174             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
175             "/xyz/openbmc_project/inventory", int32_t(0), interfaces);
176     }
177 };
178 
179 /**
180  * Chassis override class for delivering Chassis Schema
181  */
182 class Chassis : public Node
183 {
184   public:
185     Chassis(CrowApp &app) :
186         Node(app, "/redfish/v1/Chassis/<str>/", std::string())
187     {
188         entityPrivileges = {
189             {boost::beast::http::verb::get, {{"Login"}}},
190             {boost::beast::http::verb::head, {{"Login"}}},
191             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
192             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
193             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
194             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
195     }
196 
197   private:
198     /**
199      * Functions triggers appropriate requests on DBus
200      */
201     void doGet(crow::Response &res, const crow::Request &req,
202                const std::vector<std::string> &params) override
203     {
204         const std::array<const char *, 3> interfaces = {
205             "xyz.openbmc_project.Inventory.Item.Board",
206             "xyz.openbmc_project.Inventory.Item.Chassis",
207             "xyz.openbmc_project.Inventory.Item.PowerSupply"};
208 
209         // Check if there is required param, truly entering this shall be
210         // impossible.
211         if (params.size() != 1)
212         {
213             messages::internalError(res);
214             res.end();
215             return;
216         }
217         const std::string &chassisId = params[0];
218 
219         res.jsonValue["@odata.type"] = "#Chassis.v1_4_0.Chassis";
220         res.jsonValue["@odata.id"] = "/redfish/v1/Chassis/" + chassisId;
221         res.jsonValue["@odata.context"] =
222             "/redfish/v1/$metadata#Chassis.Chassis";
223         res.jsonValue["Name"] = "Chassis Collection";
224         res.jsonValue["ChassisType"] = "RackMount";
225         res.jsonValue["PowerState"] = "On";
226 
227         auto asyncResp = std::make_shared<AsyncResp>(res);
228         crow::connections::systemBus->async_method_call(
229             [asyncResp, chassisId(std::string(chassisId))](
230                 const boost::system::error_code ec,
231                 const std::vector<std::pair<
232                     std::string, std::vector<std::pair<
233                                      std::string, std::vector<std::string>>>>>
234                     &subtree) {
235                 if (ec)
236                 {
237                     messages::internalError(asyncResp->res);
238                     return;
239                 }
240                 // Iterate over all retrieved ObjectPaths.
241                 for (const std::pair<
242                          std::string,
243                          std::vector<
244                              std::pair<std::string, std::vector<std::string>>>>
245                          &object : subtree)
246                 {
247                     const std::string &path = object.first;
248                     const std::vector<
249                         std::pair<std::string, std::vector<std::string>>>
250                         &connectionNames = object.second;
251 
252                     if (!boost::ends_with(path, chassisId))
253                     {
254                         continue;
255                     }
256                     if (connectionNames.size() < 1)
257                     {
258                         BMCWEB_LOG_ERROR << "Only got "
259                                          << connectionNames.size()
260                                          << " Connection names";
261                         continue;
262                     }
263 
264                     const std::string connectionName = connectionNames[0].first;
265                     crow::connections::systemBus->async_method_call(
266                         [asyncResp, chassisId(std::string(chassisId))](
267                             const boost::system::error_code ec,
268                             const std::vector<std::pair<
269                                 std::string, VariantType>> &propertiesList) {
270                             for (const std::pair<std::string, VariantType>
271                                      &property : propertiesList)
272                             {
273                                 // Store DBus properties that are also Redfish
274                                 // properties with same name and a string value
275                                 const std::string &propertyName =
276                                     property.first;
277                                 if ((propertyName == "PartNumber") ||
278                                     (propertyName == "SerialNumber") ||
279                                     (propertyName == "Manufacturer") ||
280                                     (propertyName == "Model"))
281                                 {
282                                     const std::string *value =
283                                         std::get_if<std::string>(
284                                             &property.second);
285                                     if (value != nullptr)
286                                     {
287                                         asyncResp->res.jsonValue[propertyName] =
288                                             *value;
289                                     }
290                                 }
291                             }
292                             asyncResp->res.jsonValue["Name"] = chassisId;
293                             asyncResp->res.jsonValue["Id"] = chassisId;
294                             asyncResp->res.jsonValue["Thermal"] = {
295                                 {"@odata.id", "/redfish/v1/Chassis/" +
296                                                   chassisId + "/Thermal"}};
297                             // Power object
298                             asyncResp->res.jsonValue["Power"] = {
299                                 {"@odata.id", "/redfish/v1/Chassis/" +
300                                                   chassisId + "/Power"}};
301                             asyncResp->res.jsonValue["Status"] = {
302                                 {"Health", "OK"},
303                                 {"State", "Enabled"},
304                             };
305 
306                             asyncResp->res
307                                 .jsonValue["Links"]["ComputerSystems"] = {
308                                 {{"@odata.id", "/redfish/v1/Systems/system"}}};
309                             asyncResp->res.jsonValue["Links"]["ManagedBy"] = {
310                                 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
311                         },
312                         connectionName, path, "org.freedesktop.DBus.Properties",
313                         "GetAll",
314                         "xyz.openbmc_project.Inventory.Decorator.Asset");
315                     return;
316                 }
317 
318                 // Couldn't find an object with that name.  return an error
319                 messages::resourceNotFound(
320                     asyncResp->res, "#Chassis.v1_4_0.Chassis", chassisId);
321             },
322             "xyz.openbmc_project.ObjectMapper",
323             "/xyz/openbmc_project/object_mapper",
324             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
325             "/xyz/openbmc_project/inventory", int32_t(0), interfaces);
326 
327         getPhysicalSecurityData(asyncResp);
328     }
329 };
330 } // namespace redfish
331