xref: /openbmc/bmcweb/redfish-core/lib/chassis.hpp (revision cb103130)
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", 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 *, 2> interfaces = {
186             "xyz.openbmc_project.Inventory.Item.Board",
187             "xyz.openbmc_project.Inventory.Item.Chassis"};
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", 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 *, 2> interfaces = {
250             "xyz.openbmc_project.Inventory.Item.Board",
251             "xyz.openbmc_project.Inventory.Item.Chassis"};
252 
253         // Check if there is required param, truly entering this shall be
254         // impossible.
255         if (params.size() != 1)
256         {
257             messages::internalError(res);
258             res.end();
259             return;
260         }
261         const std::string &chassisId = params[0];
262 
263         auto asyncResp = std::make_shared<AsyncResp>(res);
264         crow::connections::systemBus->async_method_call(
265             [asyncResp, chassisId(std::string(chassisId))](
266                 const boost::system::error_code ec,
267                 const std::vector<std::pair<
268                     std::string, std::vector<std::pair<
269                                      std::string, std::vector<std::string>>>>>
270                     &subtree) {
271                 if (ec)
272                 {
273                     messages::internalError(asyncResp->res);
274                     return;
275                 }
276                 // Iterate over all retrieved ObjectPaths.
277                 for (const std::pair<
278                          std::string,
279                          std::vector<
280                              std::pair<std::string, std::vector<std::string>>>>
281                          &object : subtree)
282                 {
283                     const std::string &path = object.first;
284                     const std::vector<
285                         std::pair<std::string, std::vector<std::string>>>
286                         &connectionNames = object.second;
287 
288                     if (!boost::ends_with(path, chassisId))
289                     {
290                         continue;
291                     }
292 
293                     auto health = std::make_shared<HealthPopulate>(asyncResp);
294 
295                     crow::connections::systemBus->async_method_call(
296                         [health](const boost::system::error_code ec,
297                                  std::variant<std::vector<std::string>> &resp) {
298                             if (ec)
299                             {
300                                 return; // no sensors = no failures
301                             }
302                             std::vector<std::string> *data =
303                                 std::get_if<std::vector<std::string>>(&resp);
304                             if (data == nullptr)
305                             {
306                                 return;
307                             }
308                             health->inventory = std::move(*data);
309                         },
310                         "xyz.openbmc_project.ObjectMapper",
311                         path + "/all_sensors",
312                         "org.freedesktop.DBus.Properties", "Get",
313                         "xyz.openbmc_project.Association", "endpoints");
314 
315                     health->populate();
316 
317                     if (connectionNames.size() < 1)
318                     {
319                         BMCWEB_LOG_ERROR << "Only got "
320                                          << connectionNames.size()
321                                          << " Connection names";
322                         continue;
323                     }
324 
325                     asyncResp->res.jsonValue["@odata.type"] =
326                         "#Chassis.v1_9_0.Chassis";
327                     asyncResp->res.jsonValue["@odata.id"] =
328                         "/redfish/v1/Chassis/" + chassisId;
329                     asyncResp->res.jsonValue["@odata.context"] =
330                         "/redfish/v1/$metadata#Chassis.Chassis";
331                     asyncResp->res.jsonValue["Name"] = "Chassis Collection";
332                     asyncResp->res.jsonValue["ChassisType"] = "RackMount";
333 
334                     const std::string &connectionName =
335                         connectionNames[0].first;
336                     crow::connections::systemBus->async_method_call(
337                         [asyncResp, chassisId(std::string(chassisId))](
338                             const boost::system::error_code ec,
339                             const std::vector<std::pair<
340                                 std::string, VariantType>> &propertiesList) {
341                             for (const std::pair<std::string, VariantType>
342                                      &property : propertiesList)
343                             {
344                                 // Store DBus properties that are also Redfish
345                                 // properties with same name and a string value
346                                 const std::string &propertyName =
347                                     property.first;
348                                 if ((propertyName == "PartNumber") ||
349                                     (propertyName == "SerialNumber") ||
350                                     (propertyName == "Manufacturer") ||
351                                     (propertyName == "Model"))
352                                 {
353                                     const std::string *value =
354                                         std::get_if<std::string>(
355                                             &property.second);
356                                     if (value != nullptr)
357                                     {
358                                         asyncResp->res.jsonValue[propertyName] =
359                                             *value;
360                                     }
361                                 }
362                             }
363                             asyncResp->res.jsonValue["Name"] = chassisId;
364                             asyncResp->res.jsonValue["Id"] = chassisId;
365                             asyncResp->res.jsonValue["Thermal"] = {
366                                 {"@odata.id", "/redfish/v1/Chassis/" +
367                                                   chassisId + "/Thermal"}};
368                             // Power object
369                             asyncResp->res.jsonValue["Power"] = {
370                                 {"@odata.id", "/redfish/v1/Chassis/" +
371                                                   chassisId + "/Power"}};
372                             // SensorCollection
373                             asyncResp->res.jsonValue["Sensors"] = {
374                                 {"@odata.id", "/redfish/v1/Chassis/" +
375                                                   chassisId + "/Sensors"}};
376                             asyncResp->res.jsonValue["Status"] = {
377                                 {"State", "Enabled"},
378                             };
379 
380                             asyncResp->res
381                                 .jsonValue["Links"]["ComputerSystems"] = {
382                                 {{"@odata.id", "/redfish/v1/Systems/system"}}};
383                             asyncResp->res.jsonValue["Links"]["ManagedBy"] = {
384                                 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
385                             getChassisState(asyncResp);
386                         },
387                         connectionName, path, "org.freedesktop.DBus.Properties",
388                         "GetAll",
389                         "xyz.openbmc_project.Inventory.Decorator.Asset");
390                     return;
391                 }
392 
393                 // Couldn't find an object with that name.  return an error
394                 messages::resourceNotFound(
395                     asyncResp->res, "#Chassis.v1_9_0.Chassis", chassisId);
396             },
397             "xyz.openbmc_project.ObjectMapper",
398             "/xyz/openbmc_project/object_mapper",
399             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
400             "/xyz/openbmc_project/inventory", 0, interfaces);
401 
402         getPhysicalSecurityData(asyncResp);
403     }
404 };
405 } // namespace redfish
406