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