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