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