xref: /openbmc/bmcweb/features/redfish/lib/chassis.hpp (revision 49c53ac9b88fbf71f55076121a1605a760fa0096)
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 #ifdef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS
185         // Assume one Chassis named "chassis"
186         res.jsonValue["Members@odata.count"] = 1;
187         res.jsonValue["Members"] = {
188             {{"@odata.id", "/redfish/v1/Chassis/chassis"}}};
189         res.end();
190         return;
191 #endif
192         const std::array<const char *, 3> interfaces = {
193             "xyz.openbmc_project.Inventory.Item.Board",
194             "xyz.openbmc_project.Inventory.Item.Chassis",
195             "xyz.openbmc_project.Inventory.Item.PowerSupply"};
196 
197         auto asyncResp = std::make_shared<AsyncResp>(res);
198         crow::connections::systemBus->async_method_call(
199             [asyncResp](const boost::system::error_code ec,
200                         const std::vector<std::string> &chassisList) {
201                 if (ec)
202                 {
203                     messages::internalError(asyncResp->res);
204                     return;
205                 }
206                 nlohmann::json &chassisArray =
207                     asyncResp->res.jsonValue["Members"];
208                 chassisArray = nlohmann::json::array();
209                 for (const std::string &objpath : chassisList)
210                 {
211                     std::size_t lastPos = objpath.rfind("/");
212                     if (lastPos == std::string::npos)
213                     {
214                         BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath;
215                         continue;
216                     }
217                     chassisArray.push_back(
218                         {{"@odata.id", "/redfish/v1/Chassis/" +
219                                            objpath.substr(lastPos + 1)}});
220                 }
221 
222                 asyncResp->res.jsonValue["Members@odata.count"] =
223                     chassisArray.size();
224             },
225             "xyz.openbmc_project.ObjectMapper",
226             "/xyz/openbmc_project/object_mapper",
227             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
228             "/xyz/openbmc_project/inventory", int32_t(0), interfaces);
229     }
230 };
231 
232 /**
233  * Chassis override class for delivering Chassis Schema
234  */
235 class Chassis : public Node
236 {
237   public:
238     Chassis(CrowApp &app) :
239         Node(app, "/redfish/v1/Chassis/<str>/", std::string())
240     {
241         entityPrivileges = {
242             {boost::beast::http::verb::get, {{"Login"}}},
243             {boost::beast::http::verb::head, {{"Login"}}},
244             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
245             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
246             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
247             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
248     }
249 
250   private:
251     /**
252      * Functions triggers appropriate requests on DBus
253      */
254     void doGet(crow::Response &res, const crow::Request &req,
255                const std::vector<std::string> &params) override
256     {
257         const std::array<const char *, 3> interfaces = {
258             "xyz.openbmc_project.Inventory.Item.Board",
259             "xyz.openbmc_project.Inventory.Item.Chassis",
260             "xyz.openbmc_project.Inventory.Item.PowerSupply"};
261 
262         // Check if there is required param, truly entering this shall be
263         // impossible.
264         if (params.size() != 1)
265         {
266             messages::internalError(res);
267             res.end();
268             return;
269         }
270         const std::string &chassisId = params[0];
271 #ifdef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS
272         // In a one chassis system the only supported name is "chassis"
273         if (chassisId != "chassis")
274         {
275             messages::resourceNotFound(res, "#Chassis.v1_4_0.Chassis",
276                                        chassisId);
277             res.end();
278             return;
279         }
280 #endif
281 
282         auto asyncResp = std::make_shared<AsyncResp>(res);
283         crow::connections::systemBus->async_method_call(
284             [asyncResp, chassisId(std::string(chassisId))](
285                 const boost::system::error_code ec,
286                 const std::vector<std::pair<
287                     std::string, std::vector<std::pair<
288                                      std::string, std::vector<std::string>>>>>
289                     &subtree) {
290                 if (ec)
291                 {
292                     messages::internalError(asyncResp->res);
293                     return;
294                 }
295                 // Iterate over all retrieved ObjectPaths.
296                 for (const std::pair<
297                          std::string,
298                          std::vector<
299                              std::pair<std::string, std::vector<std::string>>>>
300                          &object : subtree)
301                 {
302                     const std::string &path = object.first;
303                     const std::vector<
304                         std::pair<std::string, std::vector<std::string>>>
305                         &connectionNames = object.second;
306 
307 // If only one chassis, just select the first one
308 #ifndef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS
309                     if (!boost::ends_with(path, chassisId))
310                     {
311                         continue;
312                     }
313 #endif
314                     if (connectionNames.size() < 1)
315                     {
316                         BMCWEB_LOG_ERROR << "Only got "
317                                          << connectionNames.size()
318                                          << " Connection names";
319                         continue;
320                     }
321 
322                     asyncResp->res.jsonValue["@odata.type"] =
323                         "#Chassis.v1_4_0.Chassis";
324                     asyncResp->res.jsonValue["@odata.id"] =
325                         "/redfish/v1/Chassis/" + chassisId;
326                     asyncResp->res.jsonValue["@odata.context"] =
327                         "/redfish/v1/$metadata#Chassis.Chassis";
328                     asyncResp->res.jsonValue["Name"] = "Chassis Collection";
329                     asyncResp->res.jsonValue["ChassisType"] = "RackMount";
330 
331                     const std::string &connectionName =
332                         connectionNames[0].first;
333                     crow::connections::systemBus->async_method_call(
334                         [asyncResp, chassisId(std::string(chassisId))](
335                             const boost::system::error_code ec,
336                             const std::vector<std::pair<
337                                 std::string, VariantType>> &propertiesList) {
338                             for (const std::pair<std::string, VariantType>
339                                      &property : propertiesList)
340                             {
341                                 // Store DBus properties that are also Redfish
342                                 // properties with same name and a string value
343                                 const std::string &propertyName =
344                                     property.first;
345                                 if ((propertyName == "PartNumber") ||
346                                     (propertyName == "SerialNumber") ||
347                                     (propertyName == "Manufacturer") ||
348                                     (propertyName == "Model"))
349                                 {
350                                     const std::string *value =
351                                         std::get_if<std::string>(
352                                             &property.second);
353                                     if (value != nullptr)
354                                     {
355                                         asyncResp->res.jsonValue[propertyName] =
356                                             *value;
357                                     }
358                                 }
359                             }
360                             asyncResp->res.jsonValue["Name"] = chassisId;
361                             asyncResp->res.jsonValue["Id"] = chassisId;
362                             asyncResp->res.jsonValue["Thermal"] = {
363                                 {"@odata.id", "/redfish/v1/Chassis/" +
364                                                   chassisId + "/Thermal"}};
365                             // Power object
366                             asyncResp->res.jsonValue["Power"] = {
367                                 {"@odata.id", "/redfish/v1/Chassis/" +
368                                                   chassisId + "/Power"}};
369                             asyncResp->res.jsonValue["Status"] = {
370                                 {"Health", "OK"},
371                                 {"State", "Enabled"},
372                             };
373 
374                             asyncResp->res
375                                 .jsonValue["Links"]["ComputerSystems"] = {
376                                 {{"@odata.id", "/redfish/v1/Systems/system"}}};
377                             asyncResp->res.jsonValue["Links"]["ManagedBy"] = {
378                                 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
379                             getChassisState(asyncResp);
380                         },
381                         connectionName, path, "org.freedesktop.DBus.Properties",
382                         "GetAll",
383                         "xyz.openbmc_project.Inventory.Decorator.Asset");
384                     return;
385                 }
386 
387                 // Couldn't find an object with that name.  return an error
388                 messages::resourceNotFound(
389                     asyncResp->res, "#Chassis.v1_4_0.Chassis", chassisId);
390             },
391             "xyz.openbmc_project.ObjectMapper",
392             "/xyz/openbmc_project/object_mapper",
393             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
394             "/xyz/openbmc_project/inventory", int32_t(0), interfaces);
395 
396         getPhysicalSecurityData(asyncResp);
397     }
398 };
399 } // namespace redfish
400