xref: /openbmc/bmcweb/redfish-core/lib/chassis.hpp (revision 09b9d45e)
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["@odata.context"] =
183             "/redfish/v1/$metadata#ChassisCollection.ChassisCollection";
184         res.jsonValue["Name"] = "Chassis Collection";
185 
186         const std::array<const char *, 2> interfaces = {
187             "xyz.openbmc_project.Inventory.Item.Board",
188             "xyz.openbmc_project.Inventory.Item.Chassis"};
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", 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 *, 2> interfaces = {
251             "xyz.openbmc_project.Inventory.Item.Board",
252             "xyz.openbmc_project.Inventory.Item.Chassis"};
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 crow::openbmc_mapper::GetSubTreeType &subtree) {
269                 if (ec)
270                 {
271                     messages::internalError(asyncResp->res);
272                     return;
273                 }
274                 // Iterate over all retrieved ObjectPaths.
275                 for (const std::pair<
276                          std::string,
277                          std::vector<
278                              std::pair<std::string, std::vector<std::string>>>>
279                          &object : subtree)
280                 {
281                     const std::string &path = object.first;
282                     const std::vector<
283                         std::pair<std::string, std::vector<std::string>>>
284                         &connectionNames = object.second;
285 
286                     if (!boost::ends_with(path, chassisId))
287                     {
288                         continue;
289                     }
290 
291                     auto health = std::make_shared<HealthPopulate>(asyncResp);
292 
293                     crow::connections::systemBus->async_method_call(
294                         [health](const boost::system::error_code ec,
295                                  std::variant<std::vector<std::string>> &resp) {
296                             if (ec)
297                             {
298                                 return; // no sensors = no failures
299                             }
300                             std::vector<std::string> *data =
301                                 std::get_if<std::vector<std::string>>(&resp);
302                             if (data == nullptr)
303                             {
304                                 return;
305                             }
306                             health->inventory = std::move(*data);
307                         },
308                         "xyz.openbmc_project.ObjectMapper",
309                         path + "/all_sensors",
310                         "org.freedesktop.DBus.Properties", "Get",
311                         "xyz.openbmc_project.Association", "endpoints");
312 
313                     health->populate();
314 
315                     if (connectionNames.size() < 1)
316                     {
317                         BMCWEB_LOG_ERROR << "Got 0 Connection names";
318                         continue;
319                     }
320 
321                     asyncResp->res.jsonValue["@odata.type"] =
322                         "#Chassis.v1_10_0.Chassis";
323                     asyncResp->res.jsonValue["@odata.id"] =
324                         "/redfish/v1/Chassis/" + chassisId;
325                     asyncResp->res.jsonValue["@odata.context"] =
326                         "/redfish/v1/$metadata#Chassis.Chassis";
327                     asyncResp->res.jsonValue["Name"] = "Chassis Collection";
328                     asyncResp->res.jsonValue["ChassisType"] = "RackMount";
329                     asyncResp->res.jsonValue["PCIeDevices"] = {
330                         {"@odata.id",
331                          "/redfish/v1/Systems/system/PCIeDevices"}};
332 
333                     const std::string &connectionName =
334                         connectionNames[0].first;
335 
336                     const std::vector<std::string> &interfaces =
337                         connectionNames[0].second;
338                     const std::array<const char *, 2> hasIndicatorLed = {
339                         "xyz.openbmc_project.Inventory.Item.Panel",
340                         "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
341 
342                     for (const char *interface : hasIndicatorLed)
343                     {
344                         if (std::find(interfaces.begin(), interfaces.end(),
345                                       interface) != interfaces.end())
346                         {
347                             getIndicatorLedState(asyncResp);
348                             break;
349                         }
350                     }
351 
352                     crow::connections::systemBus->async_method_call(
353                         [asyncResp, chassisId(std::string(chassisId))](
354                             const boost::system::error_code ec,
355                             const std::vector<std::pair<
356                                 std::string, VariantType>> &propertiesList) {
357                             for (const std::pair<std::string, VariantType>
358                                      &property : propertiesList)
359                             {
360                                 // Store DBus properties that are also Redfish
361                                 // properties with same name and a string value
362                                 const std::string &propertyName =
363                                     property.first;
364                                 if ((propertyName == "PartNumber") ||
365                                     (propertyName == "SerialNumber") ||
366                                     (propertyName == "Manufacturer") ||
367                                     (propertyName == "Model"))
368                                 {
369                                     const std::string *value =
370                                         std::get_if<std::string>(
371                                             &property.second);
372                                     if (value != nullptr)
373                                     {
374                                         asyncResp->res.jsonValue[propertyName] =
375                                             *value;
376                                     }
377                                 }
378                             }
379                             asyncResp->res.jsonValue["Name"] = chassisId;
380                             asyncResp->res.jsonValue["Id"] = chassisId;
381                             asyncResp->res.jsonValue["Thermal"] = {
382                                 {"@odata.id", "/redfish/v1/Chassis/" +
383                                                   chassisId + "/Thermal"}};
384                             // Power object
385                             asyncResp->res.jsonValue["Power"] = {
386                                 {"@odata.id", "/redfish/v1/Chassis/" +
387                                                   chassisId + "/Power"}};
388                             // SensorCollection
389                             asyncResp->res.jsonValue["Sensors"] = {
390                                 {"@odata.id", "/redfish/v1/Chassis/" +
391                                                   chassisId + "/Sensors"}};
392                             asyncResp->res.jsonValue["Status"] = {
393                                 {"State", "Enabled"},
394                             };
395 
396                             asyncResp->res
397                                 .jsonValue["Links"]["ComputerSystems"] = {
398                                 {{"@odata.id", "/redfish/v1/Systems/system"}}};
399                             asyncResp->res.jsonValue["Links"]["ManagedBy"] = {
400                                 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
401                             getChassisState(asyncResp);
402                         },
403                         connectionName, path, "org.freedesktop.DBus.Properties",
404                         "GetAll",
405                         "xyz.openbmc_project.Inventory.Decorator.Asset");
406                     return;
407                 }
408 
409                 // Couldn't find an object with that name.  return an error
410                 messages::resourceNotFound(
411                     asyncResp->res, "#Chassis.v1_10_0.Chassis", chassisId);
412             },
413             "xyz.openbmc_project.ObjectMapper",
414             "/xyz/openbmc_project/object_mapper",
415             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
416             "/xyz/openbmc_project/inventory", 0, interfaces);
417 
418         getPhysicalSecurityData(asyncResp);
419     }
420 
421     void doPatch(crow::Response &res, const crow::Request &req,
422                  const std::vector<std::string> &params) override
423     {
424         std::optional<std::string> indicatorLed;
425         auto asyncResp = std::make_shared<AsyncResp>(res);
426 
427         if (params.size() != 1)
428         {
429             return;
430         }
431 
432         if (!json_util::readJson(req, res, "IndicatorLED", indicatorLed))
433         {
434             return;
435         }
436 
437         if (!indicatorLed)
438         {
439             return; // delete this when we support more patch properties
440         }
441 
442         const std::array<const char *, 2> interfaces = {
443             "xyz.openbmc_project.Inventory.Item.Board",
444             "xyz.openbmc_project.Inventory.Item.Chassis"};
445 
446         const std::string &chassisId = params[0];
447 
448         crow::connections::systemBus->async_method_call(
449             [asyncResp, chassisId, indicatorLed](
450                 const boost::system::error_code ec,
451                 const crow::openbmc_mapper::GetSubTreeType &subtree) {
452                 if (ec)
453                 {
454                     messages::internalError(asyncResp->res);
455                     return;
456                 }
457 
458                 // Iterate over all retrieved ObjectPaths.
459                 for (const std::pair<
460                          std::string,
461                          std::vector<
462                              std::pair<std::string, std::vector<std::string>>>>
463                          &object : subtree)
464                 {
465                     const std::string &path = object.first;
466                     const std::vector<
467                         std::pair<std::string, std::vector<std::string>>>
468                         &connectionNames = object.second;
469 
470                     if (!boost::ends_with(path, chassisId))
471                     {
472                         continue;
473                     }
474 
475                     if (connectionNames.size() < 1)
476                     {
477                         BMCWEB_LOG_ERROR << "Got 0 Connection names";
478                         continue;
479                     }
480 
481                     const std::vector<std::string> &interfaces =
482                         connectionNames[0].second;
483 
484                     if (indicatorLed)
485                     {
486                         const std::array<const char *, 2> hasIndicatorLed = {
487                             "xyz.openbmc_project.Inventory.Item.Panel",
488                             "xyz.openbmc_project.Inventory.Item.Board."
489                             "Motherboard"};
490                         bool indicatorChassis = false;
491                         for (const char *interface : hasIndicatorLed)
492                         {
493                             if (std::find(interfaces.begin(), interfaces.end(),
494                                           interface) != interfaces.end())
495                             {
496                                 indicatorChassis = true;
497                                 break;
498                             }
499                         }
500                         if (indicatorChassis)
501                         {
502                             setIndicatorLedState(asyncResp,
503                                                  std::move(*indicatorLed));
504                         }
505                         else
506                         {
507                             messages::propertyUnknown(asyncResp->res,
508                                                       "IndicatorLED");
509                         }
510                     }
511                     return;
512                 }
513 
514                 messages::resourceNotFound(
515                     asyncResp->res, "#Chassis.v1_10_0.Chassis", chassisId);
516             },
517             "xyz.openbmc_project.ObjectMapper",
518             "/xyz/openbmc_project/object_mapper",
519             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
520             "/xyz/openbmc_project/inventory", 0, interfaces);
521     }
522 };
523 } // namespace redfish
524